/*
 * Copyright (c) 2011, 2012, Axel Nennker - http://axel.nennker.de/
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the names jsoncrypto, xmldap, xmldap.org, xmldap.com nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.jsoncrypto;

import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.RSAPrivateKeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;

import javax.annotation.Generated;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import junit.framework.TestCase;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OutputStream;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.asn1.DLSequence;
import org.bouncycastle.asn1.sec.SECNamedCurves;
import org.bouncycastle.asn1.x509.RSAPublicKeyStructure;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.KeyGenerationParameters;
import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.ECUtil;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoncrypto.crypto.CryptoUtils;
import org.jsoncrypto.exceptions.CryptoException;
import org.jsoncrypto.util.Base64;

public class JcBaseTest extends TestCase {
  JSONObject joeO = null;

  final String joeStr = "{\"iss\":\"joe\",\r\n" + " \"exp\":1300819380,\r\n" + " \"http://example.com/is_root\":true}";

  final String hs256 = "{\"typ\":\"JWT\",\r\n" + " \"alg\":\"HS256\"}";
  final String hs384 = "{\"typ\":\"JWT\",\r\n" + " \"alg\":\"HS384\"}";
  final String hs512 = "{\"typ\":\"JWT\",\r\n" + " \"alg\":\"HS512\"}";
  final byte[] hsKey = { 3, (byte) 35, (byte) 53, (byte) 75, (byte) 43, (byte) 15, (byte) 165, (byte) 188, (byte) 131,
      (byte) 126, (byte) 6, (byte) 101, (byte) 119, (byte) 123, (byte) 166, (byte) 143, (byte) 90, (byte) 179,
      (byte) 40, (byte) 230, (byte) 240, (byte) 84, (byte) 201, (byte) 40, (byte) 169, (byte) 15, (byte) 132,
      (byte) 178, (byte) 210, (byte) 80, (byte) 46, (byte) 191, (byte) 211, (byte) 251, (byte) 90, (byte) 146,
      (byte) 210, (byte) 6, (byte) 71, (byte) 239, (byte) 150, (byte) 138, (byte) 180, (byte) 195, (byte) 119,
      (byte) 98, (byte) 61, (byte) 34, (byte) 61, (byte) 46, (byte) 33, (byte) 114, (byte) 5, (byte) 46, (byte) 79,
      (byte) 8, (byte) 192, (byte) 205, (byte) 154, (byte) 245, (byte) 103, (byte) 208, (byte) 128, (byte) 163 };

  final String es256 = "{\"alg\":\"ES256\"}";
  final String es384 = "{\"alg\":\"ES384\"}";
  final String es512 = "{\"alg\":\"ES512\"}";

  final String rs256 = "{\"alg\":\"RS256\"}";
  final String rs384 = "{\"alg\":\"RS384\"}";
  final String rs512 = "{\"alg\":\"RS512\"}";

  final String a128kw = "{\"alg\":\"A128KW\", \"enc\":\"A128GCM\"}";
  final String a256kw = "{\"alg\":\"A256KW\", \"enc\":\"A256CBC+HS256\"}";

  final String A128GCM = "{\"alg\":\"A128GCM\"}";

  String a128kwb64;
  String a256kwb64;

  String rsa15AesGcm128HeaderStr;
  String rsa15AesGcm128HeaderStrb64;

  String rsa15AesGcm256HeaderStr;
  String rsa15AesGcm256HeaderStrb64;

  String rsaOaepAesCbc128HeaderStr;
  String rsaOaepAesCbc128HeaderStrb64;

  String rsaOaepAesCbc192HeaderStr;
  String rsaOaepAesCbc192HeaderStrb64;

  String rsaOaepAesCbc256HeaderStr;
  String rsaOaepAesCbc256HeaderStrb64;

  RSAPublicKey rsaPublicKey;
  RSAPrivateKey rsaPrivKey;

  static final byte[] ec256_1_x = { 48, (byte) 160, 66, 76, (byte) 210, 28, 41, 68, (byte) 131, (byte) 138, 45, 117,
      (byte) 201, 43, 55, (byte) 231, 110, (byte) 162, 13, (byte) 159, 0, (byte) 137, 58, 59, 78, (byte) 238,
      (byte) 138, 60, 10, (byte) 175, (byte) 236, 62 };
  static final byte[] ec256_1_y = { (byte) 224, 75, 101, (byte) 233, 36, 86, (byte) 217, (byte) 136, (byte) 139, 82,
      (byte) 179, 121, (byte) 189, (byte) 251, (byte) 213, 30, (byte) 232, 105, (byte) 239, 31, 15, (byte) 198, 91,
      102, 89, 105, 91, 108, (byte) 206, 8, 23, 35 };
  static final byte[] ec256_1_d = { (byte) 243, (byte) 189, 12, 7, (byte) 168, 31, (byte) 185, 50, 120, 30, (byte) 213,
      39, 82, (byte) 246, 12, (byte) 200, (byte) 154, 107, (byte) 229, (byte) 229, 25, 52, (byte) 254, 1, (byte) 147,
      (byte) 141, (byte) 219, 85, (byte) 216, (byte) 247, 120, 1 };

  static final String ec256_a_priv = "072f2322c0e75e0c851764b42181996778fd22592f87e5d43836097429a1c3fc";
  // static final String ec256_a_pub =
  // "04ed3c831bf3e1059f12077f4be4fdfe905573d1c67645b47d4864ea179dde9986a9a6ad34274a80fc94b3a5ef6c6e782c227a3963a6a42650976da6ade990a161";
  static final String ec256_b_priv = "1a3eda89dc067871530601f934c6428574f837507c578e45bd10a29b2e019bfb";
  // static final String ec256_b_pub =
  // "049d887ec41f36201c1868f1c09c9a93cb9d5a0ff9d08af8dde2175b25ffaa834a6caadaba025a4477beb53af076bdab597153666c70d8458c49df24713ee55e85";

  BigInteger ec256_a_D;
  BigInteger ec256_a_X;
  BigInteger ec256_a_Y;
  String ec256_a_header;
  BigInteger ec256_b_D;
  BigInteger ec256_b_X;
  BigInteger ec256_b_Y;
  String ec256_b_header;

  ECPublicKeyParameters ecPublicKeyParametersA;
  ECPrivateKeyParameters ecPrivateKeyParametersA;
  ECPublicKeyParameters ecPublicKeyParametersB;
  ECPrivateKeyParameters ecPrivateKeyParametersB;

  String ee256;

  String keybytes128B64 = null;
  String keybytes256B64 = null;

  AsymmetricCipherKeyPair eckp = null;

  public void setUp() {
    try {
      super.setUp();

      final byte[] n = { (byte) 161, (byte) 248, (byte) 22, (byte) 10, (byte) 226, (byte) 227, (byte) 201, (byte) 180,
          (byte) 101, (byte) 206, (byte) 141, (byte) 45, (byte) 101, (byte) 98, (byte) 99, (byte) 54, (byte) 43,
          (byte) 146, (byte) 125, (byte) 190, (byte) 41, (byte) 225, (byte) 240, (byte) 36, (byte) 119, (byte) 252,
          (byte) 22, (byte) 37, (byte) 204, (byte) 144, (byte) 161, (byte) 54, (byte) 227, (byte) 139, (byte) 217,
          (byte) 52, (byte) 151, (byte) 197, (byte) 182, (byte) 234, (byte) 99, (byte) 221, (byte) 119, (byte) 17,
          (byte) 230, (byte) 124, (byte) 116, (byte) 41, (byte) 249, (byte) 86, (byte) 176, (byte) 251, (byte) 138,
          (byte) 143, (byte) 8, (byte) 154, (byte) 220, (byte) 75, (byte) 105, (byte) 137, (byte) 60, (byte) 193,
          (byte) 51, (byte) 63, (byte) 83, (byte) 237, (byte) 208, (byte) 25, (byte) 184, (byte) 119, (byte) 132,
          (byte) 37, (byte) 47, (byte) 236, (byte) 145, (byte) 79, (byte) 228, (byte) 133, (byte) 119, (byte) 105,
          (byte) 89, (byte) 75, (byte) 234, (byte) 66, (byte) 128, (byte) 211, (byte) 44, (byte) 15, (byte) 85,
          (byte) 191, (byte) 98, (byte) 148, (byte) 79, (byte) 19, (byte) 3, (byte) 150, (byte) 188, (byte) 110,
          (byte) 155, (byte) 223, (byte) 110, (byte) 189, (byte) 210, (byte) 189, (byte) 163, (byte) 103, (byte) 142,
          (byte) 236, (byte) 160, (byte) 198, (byte) 104, (byte) 247, (byte) 1, (byte) 179, (byte) 141, (byte) 191,
          (byte) 251, (byte) 56, (byte) 200, (byte) 52, (byte) 44, (byte) 226, (byte) 254, (byte) 109, (byte) 39,
          (byte) 250, (byte) 222, (byte) 74, (byte) 90, (byte) 72, (byte) 116, (byte) 151, (byte) 157, (byte) 212,
          (byte) 185, (byte) 207, (byte) 154, (byte) 222, (byte) 196, (byte) 199, (byte) 91, (byte) 5, (byte) 133,
          (byte) 44, (byte) 44, (byte) 15, (byte) 94, (byte) 248, (byte) 165, (byte) 193, (byte) 117, (byte) 3,
          (byte) 146, (byte) 249, (byte) 68, (byte) 232, (byte) 237, (byte) 100, (byte) 193, (byte) 16, (byte) 198,
          (byte) 182, (byte) 71, (byte) 96, (byte) 154, (byte) 164, (byte) 120, (byte) 58, (byte) 235, (byte) 156,
          (byte) 108, (byte) 154, (byte) 215, (byte) 85, (byte) 49, (byte) 48, (byte) 80, (byte) 99, (byte) 139,
          (byte) 131, (byte) 102, (byte) 92, (byte) 111, (byte) 111, (byte) 122, (byte) 130, (byte) 163, (byte) 150,
          (byte) 112, (byte) 42, (byte) 31, (byte) 100, (byte) 27, (byte) 130, (byte) 211, (byte) 235, (byte) 242,
          (byte) 57, (byte) 34, (byte) 25, (byte) 73, (byte) 31, (byte) 182, (byte) 134, (byte) 135, (byte) 44,
          (byte) 87, (byte) 22, (byte) 245, (byte) 10, (byte) 248, (byte) 53, (byte) 141, (byte) 154, (byte) 139,
          (byte) 157, (byte) 23, (byte) 195, (byte) 64, (byte) 114, (byte) 143, (byte) 127, (byte) 135, (byte) 216,
          (byte) 154, (byte) 24, (byte) 216, (byte) 252, (byte) 171, (byte) 103, (byte) 173, (byte) 132, (byte) 89,
          (byte) 12, (byte) 46, (byte) 207, (byte) 117, (byte) 147, (byte) 57, (byte) 54, (byte) 60, (byte) 7,
          (byte) 3, (byte) 77, (byte) 111, (byte) 96, (byte) 111, (byte) 158, (byte) 33, (byte) 224, (byte) 84,
          (byte) 86, (byte) 202, (byte) 229, (byte) 233, (byte) 161 };
      final byte[] e = { 1, 0, 1 };
      final byte[] d = { 18, (byte) 174, (byte) 113, (byte) 164, (byte) 105, (byte) 205, (byte) 10, (byte) 43,
          (byte) 195, (byte) 126, (byte) 82, (byte) 108, (byte) 69, (byte) 0, (byte) 87, (byte) 31, (byte) 29,
          (byte) 97, (byte) 117, (byte) 29, (byte) 100, (byte) 233, (byte) 73, (byte) 112, (byte) 123, (byte) 98,
          (byte) 89, (byte) 15, (byte) 157, (byte) 11, (byte) 165, (byte) 124, (byte) 150, (byte) 60, (byte) 64,
          (byte) 30, (byte) 63, (byte) 207, (byte) 47, (byte) 44, (byte) 211, (byte) 189, (byte) 236, (byte) 136,
          (byte) 229, (byte) 3, (byte) 191, (byte) 198, (byte) 67, (byte) 155, (byte) 11, (byte) 40, (byte) 200,
          (byte) 47, (byte) 125, (byte) 55, (byte) 151, (byte) 103, (byte) 31, (byte) 82, (byte) 19, (byte) 238,
          (byte) 216, (byte) 193, (byte) 90, (byte) 37, (byte) 216, (byte) 213, (byte) 206, (byte) 160, (byte) 2,
          (byte) 94, (byte) 227, (byte) 171, (byte) 46, (byte) 139, (byte) 127, (byte) 121, (byte) 33, (byte) 111,
          (byte) 198, (byte) 59, (byte) 234, (byte) 86, (byte) 39, (byte) 83, (byte) 180, (byte) 6, (byte) 68,
          (byte) 198, (byte) 161, (byte) 81, (byte) 39, (byte) 217, (byte) 178, (byte) 149, (byte) 69, (byte) 64,
          (byte) 160, (byte) 187, (byte) 225, (byte) 163, (byte) 5, (byte) 86, (byte) 152, (byte) 45, (byte) 78,
          (byte) 159, (byte) 222, (byte) 95, (byte) 100, (byte) 37, (byte) 241, (byte) 77, (byte) 75, (byte) 113,
          (byte) 52, (byte) 65, (byte) 181, (byte) 93, (byte) 199, (byte) 59, (byte) 155, (byte) 74, (byte) 237,
          (byte) 204, (byte) 146, (byte) 172, (byte) 227, (byte) 146, (byte) 126, (byte) 55, (byte) 245, (byte) 125,
          (byte) 12, (byte) 253, (byte) 94, (byte) 117, (byte) 129, (byte) 250, (byte) 81, (byte) 44, (byte) 143,
          (byte) 73, (byte) 97, (byte) 169, (byte) 235, (byte) 11, (byte) 128, (byte) 248, (byte) 168, (byte) 7,
          (byte) 70, (byte) 114, (byte) 138, (byte) 85, (byte) 255, (byte) 70, (byte) 71, (byte) 31, (byte) 52,
          (byte) 37, (byte) 6, (byte) 59, (byte) 157, (byte) 83, (byte) 100, (byte) 47, (byte) 94, (byte) 222,
          (byte) 30, (byte) 132, (byte) 214, (byte) 19, (byte) 8, (byte) 26, (byte) 250, (byte) 92, (byte) 34,
          (byte) 208, (byte) 81, (byte) 40, (byte) 91, (byte) 214, (byte) 59, (byte) 148, (byte) 59, (byte) 86,
          (byte) 93, (byte) 137, (byte) 138, (byte) 5, (byte) 104, (byte) 84, (byte) 19, (byte) 229, (byte) 60,
          (byte) 60, (byte) 108, (byte) 101, (byte) 37, (byte) 255, (byte) 31, (byte) 227, (byte) 78, (byte) 61,
          (byte) 220, (byte) 112, (byte) 240, (byte) 213, (byte) 100, (byte) 80, (byte) 253, (byte) 164, (byte) 139,
          (byte) 161, (byte) 46, (byte) 16, (byte) 78, (byte) 157, (byte) 235, (byte) 159, (byte) 184, (byte) 24,
          (byte) 129, (byte) 225, (byte) 196, (byte) 189, (byte) 242, (byte) 93, (byte) 146, (byte) 71, (byte) 244,
          (byte) 80, (byte) 200, (byte) 101, (byte) 146, (byte) 121, (byte) 104, (byte) 231, (byte) 115, (byte) 52,
          (byte) 244, (byte) 65, (byte) 79, (byte) 117, (byte) 167, (byte) 80, (byte) 225, (byte) 57, (byte) 84,
          (byte) 110, (byte) 58, (byte) 138, (byte) 115, (byte) 157 };

      BigInteger N = new BigInteger(1, n);
      BigInteger E = new BigInteger(1, e);
      BigInteger D = new BigInteger(1, d);

      KeyFactory keyFactory = KeyFactory.getInstance("RSA");
      RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(N, E);
      RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(N, D);
      rsaPublicKey = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec);
      rsaPrivKey = (RSAPrivateKey) keyFactory.generatePrivate(privKeySpec);

      Digest digest = new SHA256Digest();
      byte[] bytes = rsaPublicKey.getEncoded();
      digest.update(bytes, 0, bytes.length);
      byte[] out = new byte[digest.getDigestSize()];
      /* int result = */digest.doFinal(out, 0);
      String thumbprint = Base64.encodeBytes(out, org.jsoncrypto.util.Base64.DONT_BREAK_LINES
          | org.jsoncrypto.util.Base64.URL);

      rsa15AesGcm128HeaderStr = "{\"alg\":\"" + JcBase.KW_ALG_RSA1_5 + "\",\r\n" + " \"enc\":\""
          + JcBase.ENC_ALG_A128GCM + "\",\r\n" + " \"x5t\":\"" + thumbprint + "\"}";
      rsa15AesGcm128HeaderStrb64 = Base64.encodeBytes(rsa15AesGcm128HeaderStr.getBytes("utf-8"),
          org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL);

      rsa15AesGcm256HeaderStr = "{\"alg\":\"" + JcBase.KW_ALG_RSA1_5 + "\",\r\n" + " \"enc\":\""
          + JcBase.ENC_ALG_A256GCM + "\",\r\n" + " \"x5t\":\"" + thumbprint + "\"}";
      rsa15AesGcm256HeaderStrb64 = Base64.encodeBytes(rsa15AesGcm256HeaderStr.getBytes("utf-8"),
          org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL);

      rsaOaepAesCbc128HeaderStr = "{\"alg\":\"" + JcBase.KW_ALG_RSA_OAEP + "\",\r\n" + " \"enc\":\""
          + JcBase.ENC_ALG_A128CBCHS256 + "\",\r\n" + " \"x5t\":\"" + thumbprint + "\"}";
      rsaOaepAesCbc128HeaderStrb64 = Base64.encodeBytes(rsaOaepAesCbc128HeaderStr.getBytes("utf-8"),
          org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL);

      rsaOaepAesCbc192HeaderStr = "{\"alg\":\"" + JcBase.KW_ALG_RSA_OAEP + "\",\r\n" + " \"enc\":\""
          + JcBase.ENC_ALG_A192CBCHS256 + "\",\r\n" + " \"x5t\":\"" + thumbprint + "\"}";
      rsaOaepAesCbc192HeaderStrb64 = Base64.encodeBytes(rsaOaepAesCbc192HeaderStr.getBytes("utf-8"),
          org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL);

      rsaOaepAesCbc256HeaderStr = "{\"alg\":\"" + JcBase.KW_ALG_RSA_OAEP + "\",\r\n" + " \"enc\":\""
          + JcBase.ENC_ALG_A256CBCHS256 + "\",\r\n" + " \"x5t\":\"" + thumbprint + "\"}";
      rsaOaepAesCbc256HeaderStrb64 = Base64.encodeBytes(rsaOaepAesCbc256HeaderStr.getBytes("utf-8"),
          org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL);

      a128kwb64 = Base64.encodeBytes(a128kw.getBytes("utf-8"), org.jsoncrypto.util.Base64.DONT_BREAK_LINES
          | org.jsoncrypto.util.Base64.URL);
      a256kwb64 = Base64.encodeBytes(a256kw.getBytes("utf-8"), org.jsoncrypto.util.Base64.DONT_BREAK_LINES
          | org.jsoncrypto.util.Base64.URL);

      // byte[] keybytes128 = new byte[16];
      // SecureRandom random = new SecureRandom();
      // random.nextBytes(keybytes128);
      // keybytes128B64 = Base64.encodeBytesNoBreaks(keybytes128);
      // System.out.println("keybytes128B64=" + keybytes128B64);
      //
      // byte[] keybytes256 = new byte[32];
      // random.nextBytes(keybytes256);
      // keybytes256B64 = Base64.encodeBytesNoBreaks(keybytes256);
      // System.out.println("keybytes256B64=" + keybytes256B64);

      keybytes128B64 = "wkp7v4KkBox9rSwVBXT+aA==";
      keybytes256B64 = "aRjpB3nhFA7B7B+sKwfM4OhU+6kLeg0W7p6OFbn7AfE=";

      ASN1ObjectIdentifier oid = SECNamedCurves.getOID("secp256r1");
      X9ECParameters x9ECParameters = ECUtil.getNamedCurveByOid(oid);
      // ECCurve curve = x9ECParameters.getCurve();

      {
        byte[] ec256_a_priv_bytes = Hex.decode(ec256_a_priv);
        ec256_a_D = new BigInteger(1, ec256_a_priv_bytes);
        ECPoint pubA = x9ECParameters.getG().multiply(ec256_a_D);
        ec256_a_X = pubA.getX().toBigInteger();
        ec256_a_Y = pubA.getY().toBigInteger();
        // byte[] xyBytes = Hex.decode(ec256_a_pub);
        // byte[] xBytes = new byte[xyBytes.length/2];
        // System.arraycopy(xyBytes, 0, xBytes, 0, xBytes.length);
        // ec256_a_X = new BigInteger(1, xBytes);
        // String ec256_a_X_b64 = Base64.encodeBytes(xBytes,
        // org.xmldap.util.Base64.DONT_BREAK_LINES |
        // org.xmldap.util.Base64.URL);
        String ec256_a_X_b64 = Base64.encodeBytes(ec256_a_X.toByteArray(), org.jsoncrypto.util.Base64.DONT_BREAK_LINES
            | org.jsoncrypto.util.Base64.URL);
        // byte[] yBytes = new byte[xyBytes.length/2];
        // System.arraycopy(xyBytes, xBytes.length/2, yBytes, 0,
        // yBytes.length);
        // ec256_a_Y = new BigInteger(1, yBytes);
        // String ec256_a_Y_b64 = Base64.encodeBytes(yBytes,
        // org.xmldap.util.Base64.DONT_BREAK_LINES |
        // org.xmldap.util.Base64.URL);
        String ec256_a_Y_b64 = Base64.encodeBytes(ec256_a_Y.toByteArray(), org.jsoncrypto.util.Base64.DONT_BREAK_LINES
            | org.jsoncrypto.util.Base64.URL);
        ec256_a_header = "{\"alg\":\"ECDH-ES\",\r\n" + "\"enc\":\"" + JcBase.ENC_ALG_A256GCM + "\",\r\n"
            + "\"iv\":\"__79_Pv6-fg\",\r\n" + "\"epk\": {\"jwk\": [{\r\n" + " \"crv\":\"secp256r1\",\r\n" + " \"x\":\""
            + ec256_a_X_b64 + "\",\r\n" + " \"y\":\"" + ec256_a_Y_b64 + "\"}]}}";

        ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(),
            x9ECParameters.getG(), x9ECParameters.getN(), x9ECParameters.getH(), x9ECParameters.getSeed());

        ecPublicKeyParametersA = new ECPublicKeyParameters(pubA, ecDomainParameters);
        ecPrivateKeyParametersA = new ECPrivateKeyParameters(ec256_a_D, ecDomainParameters);

      }
      {
        byte[] ec256_b_priv_bytes = Hex.decode(ec256_b_priv);
        ec256_b_D = new BigInteger(1, ec256_b_priv_bytes);
        ECPoint pubB = x9ECParameters.getG().multiply(ec256_b_D);
        ec256_b_X = pubB.getX().toBigInteger();
        ec256_b_Y = pubB.getY().toBigInteger();
        // byte[] xyBytes = Hex.decode(ec256_b_pub);
        // byte[] xBytes = new byte[xyBytes.length/2];
        // System.arraycopy(xyBytes, 0, xBytes, 0, xBytes.length);
        // ec256_b_X = new BigInteger(1, xBytes);
        String ec256_b_X_b64 = Base64.encodeBytes(ec256_b_X.toByteArray(), org.jsoncrypto.util.Base64.DONT_BREAK_LINES
            | org.jsoncrypto.util.Base64.URL);
        // String ec256_b_X_b64 = Base64.encodeBytes(xBytes,
        // org.xmldap.util.Base64.DONT_BREAK_LINES |
        // org.xmldap.util.Base64.URL);
        // byte[] yBytes = new byte[xyBytes.length/2];
        // System.arraycopy(xyBytes, xBytes.length/2, yBytes, 0,
        // yBytes.length);
        // ec256_b_Y = new BigInteger(1, yBytes);
        // String ec256_b_Y_b64 = Base64.encodeBytes(yBytes,
        // org.xmldap.util.Base64.DONT_BREAK_LINES |
        // org.xmldap.util.Base64.URL);
        String ec256_b_Y_b64 = Base64.encodeBytes(ec256_b_Y.toByteArray(), org.jsoncrypto.util.Base64.DONT_BREAK_LINES
            | org.jsoncrypto.util.Base64.URL);
        ec256_b_header = "{\"alg\":\"ECDH-ES\",\r\n" + "\"enc\":\"" + JcBase.ENC_ALG_A256GCM + "\",\r\n"
            + "\"iv\":\"--68-Ou5_ef\",\r\n" + "\"epk\": {\"jwk\": [{\r\n" + " \"crv\":\"secp256r1\",\r\n" + " \"x\":\""
            + ec256_b_X_b64 + "\",\r\n" + " \"y\":\"" + ec256_b_Y_b64 + "\"}]}}";

        ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(),
            x9ECParameters.getG(), x9ECParameters.getN(), x9ECParameters.getH(), x9ECParameters.getSeed());

        ecPublicKeyParametersB = new ECPublicKeyParameters(pubB, ecDomainParameters);
        ecPrivateKeyParametersB = new ECPrivateKeyParameters(ec256_b_D, ecDomainParameters);

      }
    } catch (Exception e) {
      assertTrue(false);
    }

  }

  public void testBadHeader() throws Exception {
    String b = "{\"alg\":\"ECDH-ES\",\"alg\":\"another alg\"}";
    try {
      @SuppressWarnings("unused")
      JSONObject j = new JSONObject(b);
      fail();
    } catch (JSONException e) {

    }
  }

  public void testJoeEncoding() throws UnsupportedEncodingException {
    byte[] bytes = joeStr.getBytes("utf-8");
    String base64urlStr = Base64.encodeBytes(bytes, org.jsoncrypto.util.Base64.DONT_BREAK_LINES
        | org.jsoncrypto.util.Base64.URL);
    String expected = "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ";
    assertEquals(expected, base64urlStr);
  }

  public void testHS256Encoding() throws UnsupportedEncodingException {
    byte[] bytes = hs256.getBytes("utf-8");
    String base64urlStr = Base64.encodeBytes(bytes, org.jsoncrypto.util.Base64.DONT_BREAK_LINES
        | org.jsoncrypto.util.Base64.URL);
    String expected = "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9";
    assertEquals(expected, base64urlStr);
  }

  public void testES256Encoding() throws UnsupportedEncodingException {
    byte[] bytes = es256.getBytes("utf-8");
    String base64urlStr = Base64.encodeBytes(bytes, org.jsoncrypto.util.Base64.DONT_BREAK_LINES
        | org.jsoncrypto.util.Base64.URL);
    String expected = "eyJhbGciOiJFUzI1NiJ9";
    assertEquals(expected, base64urlStr);
  }

  // public void testProviders() {
  // Security.addProvider(new BouncyCastleProvider());
  // Provider[] providers = java.security.Security.getProviders();
  // for (int i=0; i<providers.length; i++) {
  // Provider p = providers[i];
  // System.out.println(p.getName());
  // }
  // }

  public void testEDSAsignature_Draft01() throws Exception {
    String signature = "eyJhbGciOiJFUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q";
    byte[] x = { 127, (byte) 205, (byte) 206, (byte) 39, (byte) 112, (byte) 246, (byte) 196, (byte) 93, (byte) 65,
        (byte) 131, (byte) 203, (byte) 238, (byte) 111, (byte) 219, (byte) 75, (byte) 123, (byte) 88, (byte) 7,
        (byte) 51, (byte) 53, (byte) 123, (byte) 233, (byte) 239, (byte) 19, (byte) 186, (byte) 207, (byte) 110,
        (byte) 60, (byte) 123, (byte) 209, (byte) 84, (byte) 69 };
    byte[] y = { (byte) 199, (byte) 241, (byte) 68, (byte) 205, (byte) 27, (byte) 189, (byte) 155, (byte) 126,
        (byte) 135, (byte) 44, (byte) 223, (byte) 237, (byte) 185, (byte) 238, (byte) 185, (byte) 244, (byte) 179,
        (byte) 105, (byte) 93, (byte) 110, (byte) 169, (byte) 11, (byte) 36, (byte) 173, (byte) 138, (byte) 70,
        (byte) 35, (byte) 40, (byte) 133, (byte) 136, (byte) 229, (byte) 173 };

    assertTrue(JcBase.verify(signature, x, y));
  }

  public void testEDSAsignature256() throws Exception {
    // byte[] x = {48, (byte)160, 66, 76, (byte)210, 28, 41, 68, (byte)131,
    // (byte)138, 45, 117, (byte)201, 43, 55, (byte)231, 110, (byte)162, 13,
    // (byte)159, 0, (byte)137, 58, 59, 78, (byte)238, (byte)138, 60, 10,
    // (byte)175, (byte)236, 62};
    // byte[] y = {(byte)224, 75, 101, (byte)233, 36, 86, (byte)217,
    // (byte)136,
    // (byte)139, 82, (byte)179, 121, (byte)189, (byte)251, (byte)213, 30,
    // (byte)232, 105, (byte)239, 31, 15, (byte)198, 91, 102, 89, 105, 91,
    // 108,
    // (byte)206, 8, 23, 35};

    // "secp256r1 [NIST P-256, X9.62 prime256v1]", "1.2.840.10045.3.1.7"
    String signed = JcBase.sign(joeStr.getBytes(), new JSONObject(es256), new BigInteger(1, ec256_1_d));
    String[] split = signed.split("\\.");
    assertEquals(3, split.length);
    assertEquals("eyJhbGciOiJFUzI1NiJ9", split[0]);
    assertEquals("eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
        split[1]);
    byte[] signatureBytes = Base64.decodeUrl(split[2]);
    assertEquals(64, signatureBytes.length);
    assertTrue(JcBase.verify(signed, ec256_1_x, ec256_1_y));

  }

  private void testHMACSHA(String jwsHeaderStr, String encodedJwtHeaderSegment, String encodedJwtCryptoSegment)
      throws JcBaseException {
    String signed = JcBase.sign(joeStr.getBytes(), jwsHeaderStr, hsKey);
    String[] split = signed.split("\\.");
    assertEquals(3, split.length);
    assertEquals(encodedJwtHeaderSegment, split[0]);
    assertEquals("eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
        split[1]);
    assertEquals(encodedJwtCryptoSegment, split[2]);
    String expected = split[0] + "." + split[1] + "." + split[2];
    assertEquals(expected, signed);
  }

  public void testHS256() throws JcBaseException {
    testHMACSHA(hs256, "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9", "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk");
  }

  public void testHS384() throws JcBaseException {
    testHMACSHA(hs384, "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzM4NCJ9",
        "TUfcA4Xjq_veopvw1fiFG99UswFSMvxYisxxBb0kHQ7w8He3OkvmELPo0uy3RuR0");
  }

  public void testHS512() throws JcBaseException {
    testHMACSHA(hs512, "eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzUxMiJ9",
        "iXxB-yPnHRvriuSAfTrwz-gr5WYC6tg7gIq9JndRI9Uqn4D6twBgsJuQsQks6WqAC6OB23Lvdht79p_lA6jE8g");
  }

  private void testRSASHA(String jwtAlgorithm, String jwtHeaderSegment, String jwtCryptoSegment) throws JcBaseException {
    String signed = JcBase.sign(joeStr.getBytes(), jwtAlgorithm, rsaPrivKey);
    String[] split = signed.split("\\.");

    assertEquals(3, split.length);
    assertEquals(jwtHeaderSegment, split[0]);
    String jwtPayloadSegment = "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ";
    assertEquals(jwtPayloadSegment, split[1]);
    assertEquals(jwtCryptoSegment, split[2]);

    String expected = jwtHeaderSegment + "." + jwtPayloadSegment + "." + jwtCryptoSegment;
    assertEquals(expected, signed);
  }

  public void testRS256() throws JcBaseException {
    String jwtHeaderSegment = "eyJhbGciOiJSUzI1NiJ9";
    String jwtCryptoSegment = "cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw";
    testRSASHA(rs256, jwtHeaderSegment, jwtCryptoSegment);
  }

  public void testRS384() throws JcBaseException {
    String jwtHeaderSegment = "eyJhbGciOiJSUzM4NCJ9";
    String jwtCryptoSegment = "UqgNjrJOGhk4wfoSG6Uvrt9GcKu-TgPwInExALrMBadg1pol1uTw7mZADTddAWsC6ZzdFiTFUmIi7DuD38ftLAZoW4qezdAO7RYf1yZDsbT20bt8DJJN1I4VovL2PLg80B6x6ug-kaW8k5LaM5ce0dk1zgWhjafKC3Mb4UNLL8f9fqVMkHpdWYRjF6QjTz12Ap-gq-tPyUoWSdvzCIYOcZ9-08SQQdUTTgsNF1Qwu3TqeWPqzNJwmWHiHMmaV8I4ktMFEX-AiEBa55KsfYTx0jSbTHP-odqmnLQJ4n-oQJ2RSXy0HQP6BkdiwDHdoMUk4z_wAeOsfDTs_mLxTgOInQ";
    testRSASHA(rs384, jwtHeaderSegment, jwtCryptoSegment);
  }

  void testRsa(String name, String jwtHeaderSegment, String jwtHeaderSegmentB64, SecureRandom secureRandom)
      throws Exception {
    System.out.println("jwtHeaderSegment: " + name + " " + jwtHeaderSegment);
    System.out.println("jwtHeaderSegment: " + name + " " + jwtHeaderSegmentB64);

    String encrypted = JcBase.encrypt(joeStr.getBytes(), jwtHeaderSegment, rsaPublicKey, secureRandom);
    String[] split = encrypted.split("\\.");

    assertEquals(5, split.length);

    String newJwtHeaderSegment = new String(Base64.decodeUrl(split[0]));
    JSONObject newJwtHeader = new JSONObject(newJwtHeaderSegment);
    JSONObject oldJwtHeader = new JSONObject(jwtHeaderSegment);

    assertEquals(oldJwtHeader.get("alg"), newJwtHeader.get("alg"));
    assertEquals(oldJwtHeader.get("enc"), newJwtHeader.get("enc"));
    System.out.println(name + " jwtSymmetricKeySegment base64: " + split[1]);
    System.out.println(name + " jwtIvSegment base64: " + split[2]);
    System.out.println(name + " jwtCryptoSegment base64: " + split[3]);

    byte[] cleartextBytes = JcBase.decrypt(encrypted, rsaPrivKey);
    assertEquals(joeStr, new String(cleartextBytes));
  }

  public void testRE128Gcm() throws Exception {
    testRsa("rsa15AesGcm128", rsa15AesGcm128HeaderStr, rsa15AesGcm128HeaderStrb64, new SecureRandom());
  }

  public void testGetIntAlg() throws JcBaseException, JSONException {
    JSONObject header = new JSONObject();
    header.put("enc", "A128CBC+HS256");
    assertEquals("HS256", JcBase.getIntAlg(header));
  }

  public void testRE128() throws Exception {
    testRsa("rsaOaepAesCbc128", rsaOaepAesCbc128HeaderStr, rsaOaepAesCbc128HeaderStrb64, new SecureRandom());
  }

  public void testRE192() throws Exception {
    int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
    if (maxKeyLen < 256) {
      // don't fail. Need to install
      // Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction
      // Policy Files
      return;
    }

    testRsa("rsaOaepAesCbc192", rsaOaepAesCbc192HeaderStr, rsaOaepAesCbc192HeaderStrb64, new SecureRandom());
  }

  public void testRE256() throws Exception {
    int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
    if (maxKeyLen < 256) {
      // don't fail. Need to install
      // Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction
      // Policy Files
      return;
    }
    testRsa("rsaOaepAesCbc256", rsaOaepAesCbc256HeaderStr, rsaOaepAesCbc256HeaderStrb64, new SecureRandom());
  }

  // public void testRE256Gcm() throws Exception {
  // WebToken jwt = new WebToken(joeStr, ae128);
  // }

  public void testRE256Gcm() throws Exception {
    int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
    if (maxKeyLen < 256) {
      // don't fail. Need to install
      // Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction
      // Policy Files
      return;
    }
    testRsa("rsa15AesGcm256", rsa15AesGcm256HeaderStr, rsa15AesGcm256HeaderStrb64, new SecureRandom());
  }

  public void testRE256GcmIV() throws Exception {
    int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
    if (maxKeyLen < 256) {
      // don't fail. Need to install
      // Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction
      // Policy Files
      return;
    }
    String headerStrB64 = Base64.encodeBytes(rsa15AesGcm256HeaderStr.getBytes(),
        org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL);
    testRsa("rsa15AesGcm256 fixed IV", rsa15AesGcm256HeaderStr, headerStrB64, new SecureRandom());
  }

  public void testAE128() throws Exception {
    KeyGenerator keygen;
    try {
      keygen = KeyGenerator.getInstance("AES");
    } catch (NoSuchAlgorithmException e) {
      throw new CryptoException(e);
    }

    int keylength = 128;

    keygen.init(keylength);
    SecretKey key = keygen.generateKey();

    // String encodedIv = "AxY8DCtDaGlsbGljb3RoZQ";
    String encrypted = JcBase.encrypt(joeStr.getBytes(), a128kw, key, new SecureRandom());

    String[] split = encrypted.split("\\.");

    assertEquals(5, split.length);
    assertEquals(a128kwb64, split[0]);

    System.out.println("AES128 jwtCryptoSegment base64: " + split[1]);

    byte[] cleartext = JcBase.decrypt(encrypted, key);
    assertTrue(Arrays.equals(joeStr.getBytes(), cleartext));
  }

  // private void printBytes(String label, byte[] bytes) {
  // System.out.print(label + "\n[");
  // for (int i = 0; i < bytes.length - 1; i++) {
  // System.out.print(Integer.toString(bytes[i]) + ", ");
  // }
  // System.out.println(Integer.toString(bytes[bytes.length - 1]) + "]");
  // }

  // public void testAE192fixedKey() throws Exception {
  // WebToken jwt = new WebToken(joeStr, ae192);
  // byte[] encodedKey = new byte[] { 126, (byte) -34, (byte) -48, (byte) -34,
  // (byte) 61, (byte) 72, (byte) -63,
  // (byte) -36, (byte) 14, (byte) 53, (byte) -27, (byte) -7, (byte) -35,
  // (byte) -57, (byte) 59, (byte) -89,
  // (byte) 51, (byte) 84, (byte) 115, (byte) -119, (byte) -1, (byte) -125,
  // (byte) -115, (byte) 108 };
  //
  // SecretKey key = new SecretKeySpec(encodedKey, "AES");
  // printBytes("fixed AES192 keybytes", key.getEncoded());
  //
  // String encrypted = jwt.encrypt(key, null);
  //
  // String[] split = encrypted.split("\\.");
  //
  // assertEquals(2, split.length);
  // assertEquals(ae192b64, split[0]);
  //
  // System.out.println("AES192 jwtCryptoSegment base64: " + split[1]);
  //
  // String cleartext = WebToken.decrypt(encrypted, key);
  // assertEquals(joeStr, cleartext);
  //
  // }

  // public void testAE192() throws Exception {
  // WebToken jwt = new WebToken(joeStr, ae192);
  //
  // KeyGenerator keygen;
  // try {
  // keygen = KeyGenerator.getInstance("AES");
  // } catch (NoSuchAlgorithmException e) {
  // throw new CryptoException(e);
  // }
  //
  // keygen.init(192);
  // SecretKey key = keygen.generateKey();
  // printBytes("AES192 keybytes", key.getEncoded());
  //
  // String encrypted = jwt.encrypt(key, null);
  //
  // String[] split = encrypted.split("\\.");
  //
  // assertEquals(2, split.length);
  // assertEquals(ae192b64, split[0]);
  //
  // System.out.println("AES192 jwtCryptoSegment base64: " + split[1]);
  //
  // String cleartext = WebToken.decrypt(encrypted, key);
  // assertEquals(joeStr, cleartext);
  // }

  public void testAE256() throws Exception {
    int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
    if (maxKeyLen < 256) {
      // don't fail. Need to install
      // Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction
      // Policy Files
      return;
    }

    KeyGenerator keygen;
    try {
      keygen = KeyGenerator.getInstance("AES");
    } catch (NoSuchAlgorithmException e) {
      throw new CryptoException(e);
    }

    keygen.init(256);
    SecretKey key = keygen.generateKey();

    // String encodedIv = "AxY8DCtDaGlsbGljb3RoZQ";
    String encrypted = JcBase.encrypt(joeStr.getBytes(), a256kw, key, new SecureRandom());

    String[] split = encrypted.split("\\.");

    assertEquals(5, split.length);
    assertEquals(a256kwb64, split[0]);

    System.out.println("AES256 jwtCryptoSegment base64: " + split[1]);

    byte[] cleartext = JcBase.decrypt(encrypted, key);
    assertTrue(Arrays.equals(joeStr.getBytes(), cleartext));
  }

  public void testAgreement() throws Exception {
    ASN1ObjectIdentifier oid = SECNamedCurves.getOID("secp256r1");
    X9ECParameters x9ECParameters = ECUtil.getNamedCurveByOid(oid);
    ECCurve curve = x9ECParameters.getCurve();
    BigInteger za;
    {
      // ECPoint qB = curve.createPoint(ec256_a_X, ec256_a_Y, false);
      // ECPoint q = new ECPoint.Fp(curve, qB.getX(), qB.getY());

      ECDomainParameters ecDomainParameters = new ECDomainParameters(curve, x9ECParameters.getG(),
          x9ECParameters.getN(), x9ECParameters.getH(), x9ECParameters.getSeed());
      // ECPublicKeyParameters ecPublicKeyParameters = new
      // ECPublicKeyParameters(q, ecDomainParameters);
      ECPoint pub = x9ECParameters.getG().multiply(ec256_a_D);
      ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(pub, ecDomainParameters);

      ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(ec256_b_D, ecDomainParameters);

      ECDHBasicAgreement ecdhBasicAgreement = new ECDHBasicAgreement();
      ecdhBasicAgreement.init(ecPrivateKeyParameters);
      za = ecdhBasicAgreement.calculateAgreement(ecPublicKeyParameters);
      System.out.println("za=" + za.toString());
    }
    BigInteger zb;
    {
      // ECPoint qB = curve.createPoint(ec256_b_X, ec256_b_Y, false);
      // ECPoint q = new ECPoint.Fp(curve, qB.getX(), qB.getY());
      ECDomainParameters ecDomainParameters = new ECDomainParameters(curve, x9ECParameters.getG(),
          x9ECParameters.getN(), x9ECParameters.getH(), x9ECParameters.getSeed());
      // ECPublicKeyParameters ecPublicKeyParameters = new
      // ECPublicKeyParameters(q, ecDomainParameters);

      ECPoint pub = x9ECParameters.getG().multiply(ec256_b_D);
      ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(pub, ecDomainParameters);

      ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(ec256_a_D, ecDomainParameters);

      ECDHBasicAgreement ecdhBasicAgreement = new ECDHBasicAgreement();
      ecdhBasicAgreement.init(ecPrivateKeyParameters);
      zb = ecdhBasicAgreement.calculateAgreement(ecPublicKeyParameters);
      System.out.println("zb=" + zb.toString());
    }
    assertEquals(za.toString(), zb.toString());
  }

  private ECPrivateKeyParameters getecPrivateKeyParameters(String crv, BigInteger d) {
    ASN1ObjectIdentifier oid = SECNamedCurves.getOID(crv);
    X9ECParameters x9ECParameters = ECUtil.getNamedCurveByOid(oid);
    ECCurve curve = x9ECParameters.getCurve();
    ECDomainParameters ecDomainParameters = new ECDomainParameters(curve, x9ECParameters.getG(), x9ECParameters.getN(),
        x9ECParameters.getH(), x9ECParameters.getSeed());

    return new ECPrivateKeyParameters(d, ecDomainParameters);
  }

  public void testECencryption256() throws Exception {
    String jwtHeaderSegment = ec256_b_header;
    // String encrypted = JcBase.encrypt(joeStr.getBytes(),
    // jwtHeaderSegment, ec256_a_X, ec256_a_Y, ec256_b_D);
    ECPrivateKeyParameters ecPrivateKeyParametersB = getecPrivateKeyParameters("secp256r1", ec256_b_D);

    ECPublicKeyParameters ecPublicKeyParametersA = JcBase.getecPublicKeyParameters("secp256r1", ec256_a_X, ec256_a_Y);

    SecureRandom secureRandom = new SecureRandom();
    byte[] iv = new byte[16];
    // bits
    secureRandom.nextBytes(iv);
    String encodedIv = Base64.encodeBytes(iv, org.jsoncrypto.util.Base64.DONT_BREAK_LINES
        | org.jsoncrypto.util.Base64.URL);
    String encrypted = JcBase.encrypt(joeStr.getBytes(), jwtHeaderSegment, ecPublicKeyParametersA,
        ecPrivateKeyParametersB, encodedIv);

    String[] split = encrypted.split("\\.");

    assertEquals(5, split.length);

    String newJwtHeaderSegment = new String(Base64.decodeUrl(split[0]));
    JSONObject newJwtHeader = new JSONObject(newJwtHeaderSegment);
    JSONObject oldJwtHeader = new JSONObject(jwtHeaderSegment);

    assertEquals(oldJwtHeader.get("alg"), newJwtHeader.get("alg"));
    assertEquals(oldJwtHeader.get("enc"), newJwtHeader.get("enc"));
    System.out.println("ECDH-ES jwtHeaderSegment: " + jwtHeaderSegment);
    System.out.println("ECDH-ES jwtHeaderSegment base64: " + split[0]);
    System.out.println("ECDH-ES jwtSymmetricKeySegment base64: " + split[1]);
    System.out.println("ECDH-ES jwtIvSegment base64: " + split[2]);
    System.out.println("ECDH-ES jwtCryptoSegment base64: " + split[3]);

    ECPrivateKeyParameters ecPrivateKeyParametersA = getecPrivateKeyParameters("secp256r1", ec256_a_D);

    byte[] cleartext = JcBase.decrypt(encrypted, ecPrivateKeyParametersA);
    assertTrue(Arrays.equals(joeStr.getBytes(), cleartext));
  }

  public void testECencryptionEphemeralKey() throws Exception {
    String curveName = "secp256r1";

    ECDomainParameters ecDomainParameters;
    {
      ASN1ObjectIdentifier oid = JcBase.getNamedCurveOid(curveName);
      if (oid == null) {
        throw new NoSuchAlgorithmException("JWT EC curve: " + curveName);
      }

      X9ECParameters x9ECParameters = SECNamedCurves.getByOID(oid);
      ECCurve curve = x9ECParameters.getCurve();
      ecDomainParameters = new ECDomainParameters(curve, x9ECParameters.getG(), x9ECParameters.getN(),
          x9ECParameters.getH(), x9ECParameters.getSeed());

    }

    ECGenParameterSpec ecGenSpec = new ECGenParameterSpec(curveName);

    KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", new BouncyCastleProvider());

    g.initialize(ecGenSpec, new SecureRandom());

    // java.lang.ClassCastException:
    // org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey cannot
    // be
    // cast to org.bouncycastle.asn1.sec.ECPrivateKey

    KeyPair pair = g.generateKeyPair();
    BCECPublicKey publicKey = (BCECPublicKey) pair.getPublic();
    BCECPrivateKey privateKey = (BCECPrivateKey) pair.getPrivate();

    // Bouncycastle
    BigInteger X = publicKey.getQ().getX().toBigInteger();
    BigInteger Y = publicKey.getQ().getY().toBigInteger();
    BigInteger D = privateKey.getD();

    // JCE
    // BigInteger X = publicKey.getQ().getAffineX();
    // BigInteger Y = publicKey.getQ().getAffineY();
    // BigInteger D = privateKey.getS();
    String X_b64 = Base64.encodeBytes(X.toByteArray(), org.jsoncrypto.util.Base64.DONT_BREAK_LINES
        | org.jsoncrypto.util.Base64.URL);
    String Y_b64 = Base64.encodeBytes(Y.toByteArray(), org.jsoncrypto.util.Base64.DONT_BREAK_LINES
        | org.jsoncrypto.util.Base64.URL);
    String D_b64 = Base64.encodeBytes(D.toByteArray(), org.jsoncrypto.util.Base64.DONT_BREAK_LINES
        | org.jsoncrypto.util.Base64.URL);
    System.out.println("ECDH-ES D base64=" + D_b64);

    String encodedIv = "__79_Pv6-fg";
    String header = "{\"alg\":\"ECDH-ES\",\r\n" + "\"enc\":\"" + JcBase.ENC_ALG_A256GCM + "\",\r\n"
        + "\"epk\": {\"jwk\": [{\r\n" + " \"crv\":\"" + curveName + "\",\r\n" + " \"x\":\"" + X_b64 + "\",\r\n"
        + " \"y\":\"" + Y_b64 + "\"}]}}";

    String jwtHeaderSegment = header;
    // String encrypted = JcBase.encrypt(joeStr.getBytes(),
    // jwtHeaderSegment, ec256_b_X, ec256_b_Y, D);

    ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(D, ecDomainParameters);
    String encrypted = JcBase.encrypt(joeStr.getBytes(), jwtHeaderSegment, ecPublicKeyParametersB,
        ecPrivateKeyParameters, encodedIv);

    String[] split = encrypted.split("\\.");

    assertEquals(5, split.length);

    String newJwtHeaderSegment = new String(Base64.decodeUrl(split[0]));
    JSONObject newJwtHeader = new JSONObject(newJwtHeaderSegment);
    JSONObject oldJwtHeader = new JSONObject(jwtHeaderSegment);

    assertEquals(oldJwtHeader.get("alg"), newJwtHeader.get("alg"));
    assertEquals(oldJwtHeader.get("enc"), newJwtHeader.get("enc"));
    String ivB64 = oldJwtHeader.optString("iv", null);
    if (ivB64 != null) {
      assertEquals(ivB64, newJwtHeader.get("iv"));
    }
    System.out.println("ECDH-ES jwtHeaderSegment: " + jwtHeaderSegment);
    System.out.println("ECDH-ES jwtHeaderSegment base64: " + split[0]);
    System.out.println("ECDH-ES jwtSymmetricKeySegment base64: " + split[1]);
    System.out.println("ECDH-ES jwtIvSegment base64: " + split[2]);
    System.out.println("ECDH-ES jwtCryptoSegment base64: " + split[3]);

    ECPrivateKeyParameters ecPrivateKeyParametersB = new ECPrivateKeyParameters(ec256_b_D, ecDomainParameters);

    byte[] cleartext = JcBase.decrypt(encrypted, ecPrivateKeyParametersB);
    assertTrue(Arrays.equals(joeStr.getBytes(), cleartext));
  }

  // public void testMikesExample2() throws Exception {
  // byte[] cmk = {
  // (byte)177, (byte)161, (byte)244, (byte)128, (byte)84, (byte)143,
  // (byte)225,
  // (byte)115, (byte)63, (byte)180,
  // (byte)3, (byte)255, (byte)107, (byte)154, (byte)212, (byte)246,
  // (byte)138,
  // (byte)7, (byte)110, (byte)91,
  // (byte)112, (byte)46, (byte)34, (byte)105, (byte)47, (byte)130, (byte)203,
  // (byte)46, (byte)122, (byte)234,
  // (byte)64, (byte)252};
  // SecretKey contentEncryptionKey = new SecretKeySpec(cmk, "raw");
  // byte[] ivBytes = {
  // (byte)227, (byte)197, (byte)117, (byte)252, (byte)2, (byte)219,
  // (byte)233, (byte)68, (byte)180, (byte)225, (byte)77, (byte)219};
  // byte[] plainTextBytes = {
  // 76, 105, 118, 101, 32, 108, 111, 110, 103, 32, 97, 110, 100, 32, 112,
  // 114,
  // 111, 115, 112, 101, 114, 46};
  // String jwtHeaderStr =
  // "{\"alg\":\"RSA-OAEP\",\"enc\":\"A256GCM\",\"iv\":\"48V1_ALb6US04U3b\"}";
  // String ivBytesB64 = Base64.encodeBytes(ivBytes,
  // org.xmldap.util.Base64.DONT_BREAK_LINES | org.xmldap.util.Base64.URL);
  // assertEquals("48V1_ALb6US04U3b", ivBytesB64);
  // byte[] headerBytes = {
  // 123, 34, 97, 108, 103, 34, 58, 34, 82, 83, 65, 45, 79, 65, 69, 80,
  // 34, 44, 34, 101, 110, 99, 34, 58, 34, 65, 50, 53, 54, 71, 67, 77,
  // 34, 44, 34, 105, 118, 34, 58, 34, 52, 56, 86, 49, 95, 65, 76, 98,
  // 54, 85, 83, 48, 52, 85, 51, 98, 34, 125
  // };
  // assertEquals(jwtHeaderStr, new String(headerBytes, "UTF-8"));
  // String encodedHeader =
  // "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00iLCJpdiI6IjQ4VjFfQUxiNlVTMDRVM2IifQ";
  // assertEquals(4ncodedHeader,
  // Base64.encodeBytes(headerBytes, org.xmldap.util.Base64.DONT_BREAK_LINES |
  // org.xmldap.util.Base64.URL));
  //
  //
  // RSAPublicKey publicKey;
  // RSAPrivateKey privKey;
  // {
  // byte[] e = {1,0,1};
  // BigInteger E = new BigInteger(1, e);
  // byte[] n = {
  // (byte)161, (byte)168, (byte)84, (byte)34, (byte)133, (byte)176,
  // (byte)208,
  // (byte)173, (byte)46, (byte)176, (byte)163, (byte)110, (byte)57, (byte)30,
  // (byte)135, (byte)227,
  // (byte)9, (byte)31, (byte)226, (byte)128, (byte)84, (byte)92, (byte)116,
  // (byte)241, (byte)70, (byte)248, (byte)27, (byte)227, (byte)193, (byte)62,
  // (byte)5, (byte)91,
  // (byte)241, (byte)145, (byte)224, (byte)205, (byte)141, (byte)176,
  // (byte)184, (byte)133, (byte)239, (byte)43, (byte)81, (byte)103, (byte)9,
  // (byte)161, (byte)153, (byte)157,
  // (byte)179, (byte)104, (byte)123, (byte)51, (byte)189, (byte)34,
  // (byte)152,
  // (byte)69, (byte)97, (byte)69, (byte)78, (byte)93, (byte)140, (byte)131,
  // (byte)87, (byte)182,
  // (byte)169, (byte)101, (byte)92, (byte)142, (byte)3, (byte)22, (byte)167,
  // (byte)8, (byte)212, (byte)56, (byte)35, (byte)79, (byte)210, (byte)222,
  // (byte)192, (byte)208,
  // (byte)252, (byte)49, (byte)109, (byte)138, (byte)173, (byte)253,
  // (byte)210,
  // (byte)166, (byte)201, (byte)63, (byte)102, (byte)74, (byte)5, (byte)158,
  // (byte)41, (byte)90,
  // (byte)144, (byte)108, (byte)160, (byte)79, (byte)10, (byte)89, (byte)222,
  // (byte)231, (byte)172, (byte)31, (byte)227, (byte)197, (byte)0, (byte)19,
  // (byte)72, (byte)81,
  // (byte)138, (byte)78, (byte)136, (byte)221, (byte)121, (byte)118,
  // (byte)196,
  // (byte)17, (byte)146, (byte)10, (byte)244, (byte)188, (byte)72, (byte)113,
  // (byte)55, (byte)221,
  // (byte)162, (byte)217, (byte)171, (byte)27, (byte)57, (byte)233,
  // (byte)210,
  // (byte)101, (byte)236, (byte)154, (byte)199, (byte)56, (byte)138,
  // (byte)239,
  // (byte)101, (byte)48,
  // (byte)198, (byte)186, (byte)202, (byte)160, (byte)76, (byte)111,
  // (byte)234,
  // (byte)71, (byte)57, (byte)183, (byte)5, (byte)211, (byte)171, (byte)136,
  // (byte)126, (byte)64,
  // (byte)40, (byte)75, (byte)58, (byte)89, (byte)244, (byte)254, (byte)107,
  // (byte)84, (byte)103, (byte)7, (byte)236, (byte)69, (byte)163, (byte)18,
  // (byte)180, (byte)251,
  // (byte)58, (byte)153, (byte)46, (byte)151, (byte)174, (byte)12, (byte)103,
  // (byte)197, (byte)181, (byte)161, (byte)162, (byte)55, (byte)250,
  // (byte)235,
  // (byte)123, (byte)110,
  // (byte)17, (byte)11, (byte)158, (byte)24, (byte)47, (byte)133, (byte)8,
  // (byte)199, (byte)235, (byte)107, (byte)126, (byte)130, (byte)246,
  // (byte)73,
  // (byte)195, (byte)20,
  // (byte)108, (byte)202, (byte)176, (byte)214, (byte)187, (byte)45,
  // (byte)146,
  // (byte)182, (byte)118, (byte)54, (byte)32, (byte)200, (byte)61, (byte)201,
  // (byte)71, (byte)243,
  // (byte)1, (byte)255, (byte)131, (byte)84, (byte)37, (byte)111, (byte)211,
  // (byte)168, (byte)228, (byte)45, (byte)192, (byte)118, (byte)27,
  // (byte)197,
  // (byte)235, (byte)232,
  // (byte)36, (byte)10, (byte)230, (byte)248, (byte)190, (byte)82, (byte)182,
  // (byte)140, (byte)35, (byte)204, (byte)108, (byte)190, (byte)253,
  // (byte)186,
  // (byte)186, (byte)27
  // };
  // BigInteger N = new BigInteger(1, n);
  // byte[] d = {
  // (byte)144, (byte)183, (byte)109, (byte)34, (byte)62, (byte)134,
  // (byte)108,
  // (byte)57, (byte)44, (byte)252, (byte)10, (byte)66, (byte)73, (byte)54,
  // (byte)16, (byte)181,
  // (byte)233, (byte)92, (byte)54, (byte)219, (byte)101, (byte)42, (byte)35,
  // (byte)178, (byte)63, (byte)51, (byte)43, (byte)92, (byte)119, (byte)136,
  // (byte)251, (byte)41,
  // (byte)53, (byte)23, (byte)191, (byte)164, (byte)164, (byte)60, (byte)88,
  // (byte)227, (byte)229, (byte)152, (byte)228, (byte)213, (byte)149,
  // (byte)228, (byte)169, (byte)237,
  // (byte)104, (byte)71, (byte)151, (byte)75, (byte)88, (byte)252, (byte)216,
  // (byte)77, (byte)251, (byte)231, (byte)28, (byte)97, (byte)88, (byte)193,
  // (byte)215, (byte)202,
  // (byte)248, (byte)216, (byte)121, (byte)195, (byte)211, (byte)245,
  // (byte)250, (byte)112, (byte)71, (byte)243, (byte)61, (byte)129, (byte)95,
  // (byte)39, (byte)244, (byte)122,
  // (byte)225, (byte)217, (byte)169, (byte)211, (byte)165, (byte)48,
  // (byte)253,
  // (byte)220, (byte)59, (byte)122, (byte)219, (byte)42, (byte)86, (byte)223,
  // (byte)32, (byte)236,
  // (byte)39, (byte)48, (byte)103, (byte)78, (byte)122, (byte)216, (byte)187,
  // (byte)88, (byte)176, (byte)89, (byte)24, (byte)1, (byte)42, (byte)177,
  // (byte)24, (byte)99,
  // (byte)142, (byte)170, (byte)1, (byte)146, (byte)43, (byte)3, (byte)108,
  // (byte)64, (byte)194, (byte)121, (byte)182, (byte)95, (byte)187,
  // (byte)134,
  // (byte)71, (byte)88,
  // (byte)96, (byte)134, (byte)74, (byte)131, (byte)167, (byte)69, (byte)106,
  // (byte)143, (byte)121, (byte)27, (byte)72, (byte)44, (byte)245, (byte)95,
  // (byte)39, (byte)194,
  // (byte)179, (byte)175, (byte)203, (byte)122, (byte)16, (byte)112,
  // (byte)183,
  // (byte)17, (byte)200, (byte)202, (byte)31, (byte)17, (byte)138, (byte)156,
  // (byte)184, (byte)210,
  // (byte)157, (byte)184, (byte)154, (byte)131, (byte)128, (byte)110,
  // (byte)12,
  // (byte)85, (byte)195, (byte)122, (byte)241, (byte)79, (byte)251,
  // (byte)229,
  // (byte)183, (byte)117,
  // (byte)21, (byte)123, (byte)133, (byte)142, (byte)220, (byte)153, (byte)9,
  // (byte)59, (byte)57, (byte)105, (byte)81, (byte)255, (byte)138, (byte)77,
  // (byte)82, (byte)54,
  // (byte)62, (byte)216, (byte)38, (byte)249, (byte)208, (byte)17, (byte)197,
  // (byte)49, (byte)45, (byte)19, (byte)232, (byte)157, (byte)251, (byte)131,
  // (byte)137, (byte)175,
  // (byte)72, (byte)126, (byte)43, (byte)229, (byte)69, (byte)179, (byte)117,
  // (byte)82, (byte)157, (byte)213, (byte)83, (byte)35, (byte)57, (byte)210,
  // (byte)197, (byte)252,
  // (byte)171, (byte)143, (byte)194, (byte)11, (byte)47, (byte)163, (byte)6,
  // (byte)253, (byte)75, (byte)252, (byte)96, (byte)11, (byte)187, (byte)84,
  // (byte)130, (byte)210,
  // (byte)7, (byte)121, (byte)78, (byte)91, (byte)79, (byte)57, (byte)251,
  // (byte)138, (byte)132, (byte)220, (byte)60, (byte)224, (byte)173,
  // (byte)56,
  // (byte)224, (byte)201
  // };
  // BigInteger D = new BigInteger(1, d);
  //
  // KeyFactory keyFactory = KeyFactory.getInstance("RSA");
  // RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(N, E);
  // RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(N, D);
  // publicKey = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec);
  // privKey = (RSAPrivateKey) keyFactory.generatePrivate(privKeySpec);
  //
  // String name = "Mikes Example2";
  // System.out.println("jwtHeaderSegment: " + name + " " + jwtHeaderStr);
  // System.out.println("jwtHeaderSegment: " + name + " " + encodedHeader);
  //
  // String plainText = new String(plainTextBytes);
  // WebToken jwt = new WebToken(plainText, jwtHeaderStr);
  // String encrypted = jwt.encrypt(publicKey, contentEncryptionKey);
  //
  // String[] split = encrypted.split("\\.");
  //
  // assertEquals(5, split.length);
  //
  // String newJwtHeaderSegment = new String(Base64.decodeUrl(split[0]));
  // JSONObject newJwtHeader = new JSONObject(newJwtHeaderSegment);
  // JSONObject oldJwtHeader = new JSONObject(jwtHeaderStr);
  //
  // assertEquals(oldJwtHeader.get("alg"), newJwtHeader.get("alg"));
  // assertEquals(oldJwtHeader.get("enc"), newJwtHeader.get("enc"));
  // String ivB64 = oldJwtHeader.optString("iv", null);
  // assertEquals(ivB64, newJwtHeader.get("iv"));
  // System.out.println(name + " jwtSymmetricKeySegment base64: " + split[1]);
  // System.out.println(name + " jwtCryptoSegment base64: " + split[2]);
  //
  // String cleartext = WebToken.decrypt(encrypted, privKey);
  // assertEquals(plainText, cleartext);
  //
  // String jwe =
  // "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00iLCJpdiI6IjQ4VjFfQUxiNlVTMDRVM2IifQ.jvwoyhWxOMboB5cxX6ncAi7Wp3Q5FKRtlmIx35pfR9HpEa6Oy-iEpxEqM30W3YcRQ8WU9ouRoO5jd6tfdcpX-2X-OteHw4dnMXdMLjHGGx86LMDeFRAN2KGz7EGPJivaw0yM80fzT3zY0PKrIvU5ml1M5szqUnX4Jw0-PNcIM_j-L5YkLhv3Yk04XCwTJwxNNmXCflYAQO9f00Aa213TJJr6dbHV6I642FwU-EWvtEfN3evgX3EFIVYSnT3HCHkAAIdBQ9ykD-abRzVA_dGp_yJAZQcrZuNTqzThd_22YMPhIpzTygfC_4k7qqxI6t7Le_l5_o-taUG7vaNAl5FjEQ._e21tGGhac_peEFkLXr2dMPUZiUkrw.YbZSeHCNDZBqAdzpROlyiw";
  // split = jwe.split("\\.");
  //
  // assertEquals(5, split.length);
  // newJwtHeaderSegment = new String(Base64.decodeUrl(split[0]));
  //
  // System.out.println(name + " jwtHeader base64: " + split[0]);
  // System.out.println(name + " jwtSymmetricKeySegment base64: " + split[1]);
  // System.out.println(name + " jwtCryptoSegment base64: " + split[2]);
  // System.out.println(name + " jwtAuthnSegment base64: " + split[3]);
  //
  //
  // newJwtHeader = new JSONObject(newJwtHeaderSegment);
  // assertEquals(oldJwtHeader.get("alg"), newJwtHeader.get("alg"));
  // assertEquals(oldJwtHeader.get("enc"), newJwtHeader.get("enc"));
  // ivB64 = oldJwtHeader.optString("iv", null);
  // assertEquals(ivB64, newJwtHeader.get("iv"));
  //
  // cleartext = WebToken.decrypt(jwe, privKey);
  // assertEquals(plainText, cleartext);
  //
  // }
  // }

  //
  // http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-04#appendix-A.1
  //
  public void testJWEExampleRSAESOAEPWithIntegrity() throws Exception {
    String plaintext = "Live long and prosper.";
    byte[] plaintextBytes = { 76, 105, 118, 101, 32, 108, 111, 110, 103, 32, 97, 110, 100, 32, 112, 114, 111, 115, 112,
        101, 114, 46 };
    assertTrue(Arrays.equals(plaintext.getBytes(), plaintextBytes));

    // String ivB64 = "48V1_ALb6US04U3b";
    String headerStr = "{\"alg\":\"RSA-OAEP\",\"enc\":\"A256GCM\",\"iv\":\"48V1_ALb6US04U3b\"}";
    String headerStrB64 = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00iLCJpdiI6IjQ4VjFfQUxiNlVTMDRVM2IifQ";
    assertEquals(
        headerStrB64,
        Base64.encodeBytes(headerStr.getBytes(), org.jsoncrypto.util.Base64.DONT_BREAK_LINES
            | org.jsoncrypto.util.Base64.URL));
    byte[] cmk = { (byte) 177, (byte) 161, (byte) 244, (byte) 128, (byte) 84, (byte) 143, (byte) 225, (byte) 115,
        (byte) 63, (byte) 180, (byte) 3, (byte) 255, (byte) 107, (byte) 154, (byte) 212, (byte) 246, (byte) 138,
        (byte) 7, (byte) 110, (byte) 91, (byte) 112, (byte) 46, (byte) 34, (byte) 105, (byte) 47, (byte) 130,
        (byte) 203, (byte) 46, (byte) 122, (byte) 234, (byte) 64, (byte) 252 };

    final byte[] n = { (byte) 161, (byte) 168, (byte) 84, (byte) 34, (byte) 133, (byte) 176, (byte) 208, (byte) 173,
        (byte) 46, (byte) 176, (byte) 163, (byte) 110, (byte) 57, (byte) 30, (byte) 135, (byte) 227, (byte) 9,
        (byte) 31, (byte) 226, (byte) 128, (byte) 84, (byte) 92, (byte) 116, (byte) 241, (byte) 70, (byte) 248,
        (byte) 27, (byte) 227, (byte) 193, (byte) 62, (byte) 5, (byte) 91, (byte) 241, (byte) 145, (byte) 224,
        (byte) 205, (byte) 141, (byte) 176, (byte) 184, (byte) 133, (byte) 239, (byte) 43, (byte) 81, (byte) 103,
        (byte) 9, (byte) 161, (byte) 153, (byte) 157, (byte) 179, (byte) 104, (byte) 123, (byte) 51, (byte) 189,
        (byte) 34, (byte) 152, (byte) 69, (byte) 97, (byte) 69, (byte) 78, (byte) 93, (byte) 140, (byte) 131,
        (byte) 87, (byte) 182, (byte) 169, (byte) 101, (byte) 92, (byte) 142, (byte) 3, (byte) 22, (byte) 167,
        (byte) 8, (byte) 212, (byte) 56, (byte) 35, (byte) 79, (byte) 210, (byte) 222, (byte) 192, (byte) 208,
        (byte) 252, (byte) 49, (byte) 109, (byte) 138, (byte) 173, (byte) 253, (byte) 210, (byte) 166, (byte) 201,
        (byte) 63, (byte) 102, (byte) 74, (byte) 5, (byte) 158, (byte) 41, (byte) 90, (byte) 144, (byte) 108,
        (byte) 160, (byte) 79, (byte) 10, (byte) 89, (byte) 222, (byte) 231, (byte) 172, (byte) 31, (byte) 227,
        (byte) 197, (byte) 0, (byte) 19, (byte) 72, (byte) 81, (byte) 138, (byte) 78, (byte) 136, (byte) 221,
        (byte) 121, (byte) 118, (byte) 196, (byte) 17, (byte) 146, (byte) 10, (byte) 244, (byte) 188, (byte) 72,
        (byte) 113, (byte) 55, (byte) 221, (byte) 162, (byte) 217, (byte) 171, (byte) 27, (byte) 57, (byte) 233,
        (byte) 210, (byte) 101, (byte) 236, (byte) 154, (byte) 199, (byte) 56, (byte) 138, (byte) 239, (byte) 101,
        (byte) 48, (byte) 198, (byte) 186, (byte) 202, (byte) 160, (byte) 76, (byte) 111, (byte) 234, (byte) 71,
        (byte) 57, (byte) 183, (byte) 5, (byte) 211, (byte) 171, (byte) 136, (byte) 126, (byte) 64, (byte) 40,
        (byte) 75, (byte) 58, (byte) 89, (byte) 244, (byte) 254, (byte) 107, (byte) 84, (byte) 103, (byte) 7,
        (byte) 236, (byte) 69, (byte) 163, (byte) 18, (byte) 180, (byte) 251, (byte) 58, (byte) 153, (byte) 46,
        (byte) 151, (byte) 174, (byte) 12, (byte) 103, (byte) 197, (byte) 181, (byte) 161, (byte) 162, (byte) 55,
        (byte) 250, (byte) 235, (byte) 123, (byte) 110, (byte) 17, (byte) 11, (byte) 158, (byte) 24, (byte) 47,
        (byte) 133, (byte) 8, (byte) 199, (byte) 235, (byte) 107, (byte) 126, (byte) 130, (byte) 246, (byte) 73,
        (byte) 195, (byte) 20, (byte) 108, (byte) 202, (byte) 176, (byte) 214, (byte) 187, (byte) 45, (byte) 146,
        (byte) 182, (byte) 118, (byte) 54, (byte) 32, (byte) 200, (byte) 61, (byte) 201, (byte) 71, (byte) 243,
        (byte) 1, (byte) 255, (byte) 131, (byte) 84, (byte) 37, (byte) 111, (byte) 211, (byte) 168, (byte) 228,
        (byte) 45, (byte) 192, (byte) 118, (byte) 27, (byte) 197, (byte) 235, (byte) 232, (byte) 36, (byte) 10,
        (byte) 230, (byte) 248, (byte) 190, (byte) 82, (byte) 182, (byte) 140, (byte) 35, (byte) 204, (byte) 108,
        (byte) 190, (byte) 253, (byte) 186, (byte) 186, (byte) 27 };
    final byte[] e = { 1, 0, 1 };
    final byte[] d = { (byte) 144, (byte) 183, (byte) 109, (byte) 34, (byte) 62, (byte) 134, (byte) 108, (byte) 57,
        (byte) 44, (byte) 252, (byte) 10, (byte) 66, (byte) 73, (byte) 54, (byte) 16, (byte) 181, (byte) 233,
        (byte) 92, (byte) 54, (byte) 219, (byte) 101, (byte) 42, (byte) 35, (byte) 178, (byte) 63, (byte) 51,
        (byte) 43, (byte) 92, (byte) 119, (byte) 136, (byte) 251, (byte) 41, (byte) 53, (byte) 23, (byte) 191,
        (byte) 164, (byte) 164, (byte) 60, (byte) 88, (byte) 227, (byte) 229, (byte) 152, (byte) 228, (byte) 213,
        (byte) 149, (byte) 228, (byte) 169, (byte) 237, (byte) 104, (byte) 71, (byte) 151, (byte) 75, (byte) 88,
        (byte) 252, (byte) 216, (byte) 77, (byte) 251, (byte) 231, (byte) 28, (byte) 97, (byte) 88, (byte) 193,
        (byte) 215, (byte) 202, (byte) 248, (byte) 216, (byte) 121, (byte) 195, (byte) 211, (byte) 245, (byte) 250,
        (byte) 112, (byte) 71, (byte) 243, (byte) 61, (byte) 129, (byte) 95, (byte) 39, (byte) 244, (byte) 122,
        (byte) 225, (byte) 217, (byte) 169, (byte) 211, (byte) 165, (byte) 48, (byte) 253, (byte) 220, (byte) 59,
        (byte) 122, (byte) 219, (byte) 42, (byte) 86, (byte) 223, (byte) 32, (byte) 236, (byte) 39, (byte) 48,
        (byte) 103, (byte) 78, (byte) 122, (byte) 216, (byte) 187, (byte) 88, (byte) 176, (byte) 89, (byte) 24,
        (byte) 1, (byte) 42, (byte) 177, (byte) 24, (byte) 99, (byte) 142, (byte) 170, (byte) 1, (byte) 146, (byte) 43,
        (byte) 3, (byte) 108, (byte) 64, (byte) 194, (byte) 121, (byte) 182, (byte) 95, (byte) 187, (byte) 134,
        (byte) 71, (byte) 88, (byte) 96, (byte) 134, (byte) 74, (byte) 131, (byte) 167, (byte) 69, (byte) 106,
        (byte) 143, (byte) 121, (byte) 27, (byte) 72, (byte) 44, (byte) 245, (byte) 95, (byte) 39, (byte) 194,
        (byte) 179, (byte) 175, (byte) 203, (byte) 122, (byte) 16, (byte) 112, (byte) 183, (byte) 17, (byte) 200,
        (byte) 202, (byte) 31, (byte) 17, (byte) 138, (byte) 156, (byte) 184, (byte) 210, (byte) 157, (byte) 184,
        (byte) 154, (byte) 131, (byte) 128, (byte) 110, (byte) 12, (byte) 85, (byte) 195, (byte) 122, (byte) 241,
        (byte) 79, (byte) 251, (byte) 229, (byte) 183, (byte) 117, (byte) 21, (byte) 123, (byte) 133, (byte) 142,
        (byte) 220, (byte) 153, (byte) 9, (byte) 59, (byte) 57, (byte) 105, (byte) 81, (byte) 255, (byte) 138,
        (byte) 77, (byte) 82, (byte) 54, (byte) 62, (byte) 216, (byte) 38, (byte) 249, (byte) 208, (byte) 17,
        (byte) 197, (byte) 49, (byte) 45, (byte) 19, (byte) 232, (byte) 157, (byte) 251, (byte) 131, (byte) 137,
        (byte) 175, (byte) 72, (byte) 126, (byte) 43, (byte) 229, (byte) 69, (byte) 179, (byte) 117, (byte) 82,
        (byte) 157, (byte) 213, (byte) 83, (byte) 35, (byte) 57, (byte) 210, (byte) 197, (byte) 252, (byte) 171,
        (byte) 143, (byte) 194, (byte) 11, (byte) 47, (byte) 163, (byte) 6, (byte) 253, (byte) 75, (byte) 252,
        (byte) 96, (byte) 11, (byte) 187, (byte) 84, (byte) 130, (byte) 210, (byte) 7, (byte) 121, (byte) 78,
        (byte) 91, (byte) 79, (byte) 57, (byte) 251, (byte) 138, (byte) 132, (byte) 220, (byte) 60, (byte) 224,
        (byte) 173, (byte) 56, (byte) 224, (byte) 201 };

    BigInteger N = new BigInteger(1, n);
    BigInteger E = new BigInteger(1, e);
    BigInteger D = new BigInteger(1, d);

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(N, E);
    RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(N, D);
    RSAPublicKey aRsaPublicKey = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec);
    RSAPrivateKey aRsaPrivKey = (RSAPrivateKey) keyFactory.generatePrivate(privKeySpec);

    SecretKey contentEncryptionKey = new SecretKeySpec(cmk, "AES");
    SecureRandom secureRandom = new SecureRandom();
    byte[] iv = new byte[16];
    // bits
    secureRandom.nextBytes(iv);
    String encodedIv = Base64.encodeBytes(iv, org.jsoncrypto.util.Base64.DONT_BREAK_LINES
        | org.jsoncrypto.util.Base64.URL);
    String encrypted = JcBase.encrypt(plaintext.getBytes(), headerStr, aRsaPublicKey, contentEncryptionKey, encodedIv);
    String[] split = encrypted.split("\\.");
    assertEquals(5, split.length);

    byte[] cleartextBytes = JcBase.decrypt(encrypted, aRsaPrivKey);
    assertTrue(Arrays.equals(plaintextBytes, cleartextBytes));
  }

  public void testAESKW() throws Exception {
    byte[] toEncrypt = "some text".getBytes(); // or
                                               // "{\"code\":\"abcd1234\"}".getBytes()
    String a128kw = "{\"alg\":\"A128KW\", \"enc\":\"A128GCM\"}";
    SecretKey key = new SecretKeySpec("password12345678".getBytes(), "AES");
    // String encodedIv = null;
    String encrypted = JcBase.encrypt(toEncrypt, a128kw, key, new SecureRandom());
    String[] split = encrypted.split("\\.");
    assertEquals(5, split.length);
    String encodedJweHeader = split[0];
    byte[] x = Base64.decodeUrl(encodedJweHeader);
    System.out.println("header=" + new String(x));
    byte[] decrypted = JcBase.decrypt(encrypted, key);
    System.out.println("clear text:" + new String(decrypted));
  }

  //
  // http://tools.ietf.org/html/draft-ietf-jose-json-web-encryption-04#appendix-A.2
  //
  public void testJWEExampleRSAESPKCS1V1_5andAESCBC() throws Exception {
    String plaintext = "Now is the time for all good men to come to the aid of their country.";
    byte[] plaintextBytes = { 78, 111, 119, 32, 105, 115, 32, 116, 104, 101, 32, 116, 105, 109, 101, 32, 102, 111, 114,
        32, 97, 108, 108, 32, 103, 111, 111, 100, 32, 109, 101, 110, 32, 116, 111, 32, 99, 111, 109, 101, 32, 116, 111,
        32, 116, 104, 101, 32, 97, 105, 100, 32, 111, 102, 32, 116, 104, 101, 105, 114, 32, 99, 111, 117, 110, 116,
        114, 121, 46 };
    assertTrue(Arrays.equals(plaintext.getBytes(), plaintextBytes));

    String headerStr = "{\"alg\":\"RSA1_5\",\"enc\":\"A128CBC+HS256\"}";
    String headerStrB64 = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDK0hTMjU2In0";
    assertEquals(
        headerStrB64,
        Base64.encodeBytes(headerStr.getBytes(), org.jsoncrypto.util.Base64.DONT_BREAK_LINES
            | org.jsoncrypto.util.Base64.URL));

    final byte[] cmk = { (byte) 4, (byte) 211, (byte) 31, (byte) 197, (byte) 84, (byte) 157, (byte) 252, (byte) 254,
        (byte) 11, (byte) 100, (byte) 157, (byte) 250, (byte) 63, (byte) 170, (byte) 106, (byte) 206, (byte) 107,
        (byte) 124, (byte) 212, (byte) 45, (byte) 111, (byte) 107, (byte) 9, (byte) 219, (byte) 200, (byte) 177,
        (byte) 0, (byte) 240, (byte) 143, (byte) 156, (byte) 44, (byte) 207 };
    final byte[] n = { (byte) 177, (byte) 119, (byte) 33, (byte) 13, (byte) 164, (byte) 30, (byte) 108, (byte) 121,
        (byte) 207, (byte) 136, (byte) 107, (byte) (byte) 242, (byte) 12, (byte) 224, (byte) 19, (byte) 226,
        (byte) 198, (byte) 134, (byte) 17, (byte) 71, (byte) 173, (byte) 75, (byte) 42, (byte) (byte) 61, (byte) 48,
        (byte) 162, (byte) 206, (byte) 161, (byte) 97, (byte) 108, (byte) 185, (byte) 234, (byte) 226, (byte) 219,
        (byte) (byte) 118, (byte) 206, (byte) 118, (byte) 5, (byte) 169, (byte) 224, (byte) 60, (byte) 181, (byte) 90,
        (byte) 85, (byte) 51, (byte) 123, (byte) (byte) 6, (byte) 224, (byte) 4, (byte) 122, (byte) 29, (byte) 230,
        (byte) 151, (byte) 12, (byte) 244, (byte) 127, (byte) 121, (byte) 25, (byte) (byte) 4, (byte) 85, (byte) 220,
        (byte) 144, (byte) 215, (byte) 110, (byte) 130, (byte) 17, (byte) 68, (byte) 228, (byte) 129,
        (byte) (byte) 138, (byte) 7, (byte) 130, (byte) 231, (byte) 40, (byte) 212, (byte) 214, (byte) 17, (byte) 179,
        (byte) 28, (byte) 124, (byte) (byte) 151, (byte) 178, (byte) 207, (byte) 20, (byte) 14, (byte) 154, (byte) 222,
        (byte) 113, (byte) 176, (byte) 24, (byte) 198, (byte) (byte) 73, (byte) 211, (byte) 113, (byte) 9, (byte) 33,
        (byte) 178, (byte) 80, (byte) 13, (byte) 25, (byte) 21, (byte) 25, (byte) 153, (byte) (byte) 212, (byte) 206,
        (byte) 67, (byte) 154, (byte) 147, (byte) 70, (byte) 194, (byte) 192, (byte) 183, (byte) 160, (byte) 83,
        (byte) (byte) 98, (byte) 236, (byte) 175, (byte) 85, (byte) 23, (byte) 97, (byte) 75, (byte) 199, (byte) 177,
        (byte) 73, (byte) 145, (byte) 50, (byte) (byte) 253, (byte) 206, (byte) 32, (byte) 179, (byte) 254, (byte) 236,
        (byte) 190, (byte) 82, (byte) 73, (byte) 67, (byte) 129, (byte) (byte) 253, (byte) 252, (byte) 220, (byte) 108,
        (byte) 136, (byte) 138, (byte) 11, (byte) 192, (byte) 1, (byte) 36, (byte) 239, (byte) (byte) 228, (byte) 55,
        (byte) 81, (byte) 113, (byte) 17, (byte) 25, (byte) 140, (byte) 63, (byte) 239, (byte) 146, (byte) 3,
        (byte) 172, (byte) (byte) 96, (byte) 60, (byte) 227, (byte) 233, (byte) 64, (byte) 255, (byte) 224, (byte) 173,
        (byte) 225, (byte) 228, (byte) 229, (byte) (byte) 92, (byte) 112, (byte) 72, (byte) 99, (byte) 97, (byte) 26,
        (byte) 87, (byte) 187, (byte) 123, (byte) 46, (byte) 50, (byte) 90, (byte) (byte) 202, (byte) 117, (byte) 73,
        (byte) 10, (byte) 153, (byte) 47, (byte) 224, (byte) 178, (byte) 163, (byte) 77, (byte) 48, (byte) 46,
        (byte) (byte) 154, (byte) 33, (byte) 148, (byte) 34, (byte) 228, (byte) 33, (byte) 172, (byte) 216, (byte) 89,
        (byte) 46, (byte) 225, (byte) (byte) 127, (byte) 68, (byte) 146, (byte) 234, (byte) 30, (byte) 147, (byte) 54,
        (byte) 146, (byte) 5, (byte) 133, (byte) 45, (byte) 78, (byte) (byte) 254, (byte) 85, (byte) 55, (byte) 75,
        (byte) 213, (byte) 86, (byte) 194, (byte) 218, (byte) 215, (byte) 163, (byte) 189, (byte) (byte) 194,
        (byte) 54, (byte) 6, (byte) 83, (byte) 36, (byte) 18, (byte) 153, (byte) 53, (byte) 7, (byte) 48, (byte) 89,
        (byte) 35, (byte) 66, (byte) (byte) 144, (byte) 7, (byte) 65, (byte) 154, (byte) 13, (byte) 97, (byte) 75,
        (byte) 55, (byte) 230, (byte) 132, (byte) 3, (byte) 13, (byte) (byte) 239, (byte) 71 };
    final byte[] e = { 1, 0, 1 };
    final byte[] d = { (byte) 84, (byte) 80, (byte) 150, (byte) 58, (byte) 165, (byte) 235, (byte) 242, (byte) 123,
        (byte) 217, (byte) 55, (byte) 38, (byte) (byte) 154, (byte) 36, (byte) 181, (byte) 221, (byte) 156, (byte) 211,
        (byte) 215, (byte) 100, (byte) 164, (byte) 90, (byte) 88, (byte) (byte) 40, (byte) 228, (byte) 83, (byte) 148,
        (byte) 54, (byte) 122, (byte) 4, (byte) 16, (byte) 165, (byte) 48, (byte) 76, (byte) 194, (byte) (byte) 26,
        (byte) 107, (byte) 51, (byte) 53, (byte) 179, (byte) 165, (byte) 31, (byte) 18, (byte) 198, (byte) 173,
        (byte) 78, (byte) 61, (byte) (byte) 56, (byte) 97, (byte) 252, (byte) 158, (byte) 140, (byte) 80, (byte) 63,
        (byte) 25, (byte) 223, (byte) 156, (byte) 36, (byte) 203, (byte) (byte) 214, (byte) 252, (byte) 120, (byte) 67,
        (byte) 180, (byte) 167, (byte) 3, (byte) 82, (byte) 243, (byte) 25, (byte) 97, (byte) 214, (byte) (byte) 83,
        (byte) 133, (byte) 69, (byte) 16, (byte) 104, (byte) 54, (byte) 160, (byte) 200, (byte) 41, (byte) 83,
        (byte) 164, (byte) 187, (byte) (byte) 70, (byte) 153, (byte) 111, (byte) 234, (byte) 242, (byte) 158,
        (byte) 175, (byte) 28, (byte) 198, (byte) 48, (byte) 211, (byte) (byte) 45, (byte) 148, (byte) 58, (byte) 23,
        (byte) 62, (byte) 227, (byte) 74, (byte) 52, (byte) 117, (byte) 42, (byte) 90, (byte) 41, (byte) (byte) 249,
        (byte) 130, (byte) 154, (byte) 80, (byte) 119, (byte) 61, (byte) 26, (byte) 193, (byte) 40, (byte) 125,
        (byte) 10, (byte) (byte) 152, (byte) 174, (byte) 227, (byte) 225, (byte) 205, (byte) 32, (byte) 62, (byte) 66,
        (byte) 6, (byte) 163, (byte) 100, (byte) 99, (byte) (byte) 219, (byte) 19, (byte) 253, (byte) 25, (byte) 105,
        (byte) 80, (byte) 201, (byte) 29, (byte) 252, (byte) 157, (byte) 237, (byte) (byte) 69, (byte) 1, (byte) 80,
        (byte) 171, (byte) 167, (byte) 20, (byte) 196, (byte) 156, (byte) 109, (byte) 249, (byte) 88, (byte) 0,
        (byte) (byte) 3, (byte) 152, (byte) 38, (byte) 165, (byte) 72, (byte) 87, (byte) 6, (byte) 152, (byte) 71,
        (byte) 156, (byte) 214, (byte) 16, (byte) (byte) 71, (byte) 30, (byte) 82, (byte) 51, (byte) 103, (byte) 76,
        (byte) 218, (byte) 63, (byte) 9, (byte) 84, (byte) 163, (byte) 249, (byte) (byte) 91, (byte) 215, (byte) 44,
        (byte) 238, (byte) 85, (byte) 101, (byte) 240, (byte) 148, (byte) 1, (byte) 82, (byte) 224, (byte) 91,
        (byte) (byte) 135, (byte) 105, (byte) 127, (byte) 84, (byte) 171, (byte) 181, (byte) 152, (byte) 210,
        (byte) 183, (byte) 126, (byte) 24, (byte) (byte) 46, (byte) 196, (byte) 90, (byte) 173, (byte) 38, (byte) 245,
        (byte) 219, (byte) 186, (byte) 222, (byte) 27, (byte) 240, (byte) (byte) 212, (byte) 194, (byte) 15, (byte) 66,
        (byte) 135, (byte) 226, (byte) 178, (byte) 190, (byte) 52, (byte) 245, (byte) 74, (byte) (byte) 65, (byte) 224,
        (byte) 81, (byte) 100, (byte) 85, (byte) 25, (byte) 204, (byte) 165, (byte) 203, (byte) 187, (byte) 175,
        (byte) (byte) 84, (byte) 100, (byte) 82, (byte) 15, (byte) 11, (byte) 23, (byte) 202, (byte) 151, (byte) 107,
        (byte) 54, (byte) 41, (byte) 207, (byte) (byte) 3, (byte) 136, (byte) 229, (byte) 134, (byte) 131, (byte) 93,
        (byte) 139, (byte) 50, (byte) 182, (byte) 204, (byte) 93, (byte) (byte) 130, (byte) 89 };

    BigInteger N = new BigInteger(1, n);
    BigInteger E = new BigInteger(1, e);
    BigInteger D = new BigInteger(1, d);

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(N, E);
    RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(N, D);
    RSAPublicKey aRsaPublicKey = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec);
    RSAPrivateKey aRsaPrivKey = (RSAPrivateKey) keyFactory.generatePrivate(privKeySpec);

    SecretKey contentEncryptionKey = new SecretKeySpec(cmk, "AES");
    String encodedStr = "AxY8DCtDaGlsbGljb3RoZQ";
    String encrypted = JcBase.encrypt(plaintext.getBytes(), headerStr, aRsaPublicKey, contentEncryptionKey, encodedStr);
    String[] split = encrypted.split("\\.");
    assertEquals(5, split.length);

    byte[] cleartextBytes = JcBase.decrypt(encrypted, aRsaPrivKey);
    assertTrue(Arrays.equals(plaintextBytes, cleartextBytes));

    String ivStr = "AxY8DCtDaGlsbGljb3RoZQ";
    byte[] ivBytes = Base64.decodeUrl(ivStr);
    assertEquals(16, ivBytes.length);

    // String webToken =
    // "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDK0hTMjU2In0.Mw_3ejHF0lmjYXsPbJcATEAxvQugI7lZou6GMB_FrGE9rz5H9oOnKC4G3V9H6huoyDLyocNbHJiWYKncU1k4BYYnOQKCZPKKocPzreYEzZba_XiBceTQ3ltHJAyzWN46YGvrQ9SoW0Wsc-oYiZLz-m0Wu9HfFDJtEisLJN10mDwmN1ZzcjCHyH1SvTkZQxbPAxkRsJQXph8CuKsYnfi43aHm7g_NfSw8T88EQpN8lxEHeRsQaHzjzOdDXtIyKh9VFslN5Mz-7LhJio-Wqqrr8aG1X6dkc-r-vcN3yI67Ov-5AatzJIxarUajds0i30jqCViu7slQ-803ClYmZOdu5A.AxY8DCtDaGlsbGljb3RoZQ._Z_djlIoC4MDSCKireWS2beti4Q6iSG2UjFujQvdz-_PQdUcFNkOulegD6BgjgdFLjeB4HHOO7UHvP8PEDu0a0sA2a_-CI0w2YQQ2QQe35M.fE8IGdUsui9Z13-UdNC5--zvimuE3ojQSV28d6Z40Pk";
    //
    // byte[] x = JcBase.decrypt(webToken, aRsaPrivKey);
    // assertTrue(Arrays.equals(plaintextBytes, x));
  }

  public void testJWEExampleRSAESPKCS1V1_5andAESCBC256() throws Exception {
    int maxKeyLen = Cipher.getMaxAllowedKeyLength("AES");
    if (maxKeyLen < 256) {
      // don't fail. Need to install
      // Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction
      // Policy Files
      return;
    }

    String plaintext = "Now is the time for all good men to come to the aid of their country.";
    byte[] plaintextBytes = { 78, 111, 119, 32, 105, 115, 32, 116, 104, 101, 32, 116, 105, 109, 101, 32, 102, 111, 114,
        32, 97, 108, 108, 32, 103, 111, 111, 100, 32, 109, 101, 110, 32, 116, 111, 32, 99, 111, 109, 101, 32, 116, 111,
        32, 116, 104, 101, 32, 97, 105, 100, 32, 111, 102, 32, 116, 104, 101, 105, 114, 32, 99, 111, 117, 110, 116,
        114, 121, 46 };
    assertTrue(Arrays.equals(plaintext.getBytes(), plaintextBytes));

    String headerStr = "{\"alg\":\"RSA1_5\",\"enc\":\"A256CBC+HS256\"}";
    String headerStrB64 = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMjU2Q0JDK0hTMjU2In0";
    assertEquals(
        headerStrB64,
        Base64.encodeBytes(headerStr.getBytes(), org.jsoncrypto.util.Base64.DONT_BREAK_LINES
            | org.jsoncrypto.util.Base64.URL));

    final byte[] cmk = { (byte) 4, (byte) 211, (byte) 31, (byte) 197, (byte) 84, (byte) 157, (byte) 252, (byte) 254,
        (byte) 11, (byte) 100, (byte) 157, (byte) 250, (byte) 63, (byte) 170, (byte) 106, (byte) 206, (byte) 107,
        (byte) 124, (byte) 212, (byte) 45, (byte) 111, (byte) 107, (byte) 9, (byte) 219, (byte) 200, (byte) 177,
        (byte) 0, (byte) 240, (byte) 143, (byte) 156, (byte) 44, (byte) 207 };
    final byte[] n = { (byte) 177, (byte) 119, (byte) 33, (byte) 13, (byte) 164, (byte) 30, (byte) 108, (byte) 121,
        (byte) 207, (byte) 136, (byte) 107, (byte) (byte) 242, (byte) 12, (byte) 224, (byte) 19, (byte) 226,
        (byte) 198, (byte) 134, (byte) 17, (byte) 71, (byte) 173, (byte) 75, (byte) 42, (byte) (byte) 61, (byte) 48,
        (byte) 162, (byte) 206, (byte) 161, (byte) 97, (byte) 108, (byte) 185, (byte) 234, (byte) 226, (byte) 219,
        (byte) (byte) 118, (byte) 206, (byte) 118, (byte) 5, (byte) 169, (byte) 224, (byte) 60, (byte) 181, (byte) 90,
        (byte) 85, (byte) 51, (byte) 123, (byte) (byte) 6, (byte) 224, (byte) 4, (byte) 122, (byte) 29, (byte) 230,
        (byte) 151, (byte) 12, (byte) 244, (byte) 127, (byte) 121, (byte) 25, (byte) (byte) 4, (byte) 85, (byte) 220,
        (byte) 144, (byte) 215, (byte) 110, (byte) 130, (byte) 17, (byte) 68, (byte) 228, (byte) 129,
        (byte) (byte) 138, (byte) 7, (byte) 130, (byte) 231, (byte) 40, (byte) 212, (byte) 214, (byte) 17, (byte) 179,
        (byte) 28, (byte) 124, (byte) (byte) 151, (byte) 178, (byte) 207, (byte) 20, (byte) 14, (byte) 154, (byte) 222,
        (byte) 113, (byte) 176, (byte) 24, (byte) 198, (byte) (byte) 73, (byte) 211, (byte) 113, (byte) 9, (byte) 33,
        (byte) 178, (byte) 80, (byte) 13, (byte) 25, (byte) 21, (byte) 25, (byte) 153, (byte) (byte) 212, (byte) 206,
        (byte) 67, (byte) 154, (byte) 147, (byte) 70, (byte) 194, (byte) 192, (byte) 183, (byte) 160, (byte) 83,
        (byte) (byte) 98, (byte) 236, (byte) 175, (byte) 85, (byte) 23, (byte) 97, (byte) 75, (byte) 199, (byte) 177,
        (byte) 73, (byte) 145, (byte) 50, (byte) (byte) 253, (byte) 206, (byte) 32, (byte) 179, (byte) 254, (byte) 236,
        (byte) 190, (byte) 82, (byte) 73, (byte) 67, (byte) 129, (byte) (byte) 253, (byte) 252, (byte) 220, (byte) 108,
        (byte) 136, (byte) 138, (byte) 11, (byte) 192, (byte) 1, (byte) 36, (byte) 239, (byte) (byte) 228, (byte) 55,
        (byte) 81, (byte) 113, (byte) 17, (byte) 25, (byte) 140, (byte) 63, (byte) 239, (byte) 146, (byte) 3,
        (byte) 172, (byte) (byte) 96, (byte) 60, (byte) 227, (byte) 233, (byte) 64, (byte) 255, (byte) 224, (byte) 173,
        (byte) 225, (byte) 228, (byte) 229, (byte) (byte) 92, (byte) 112, (byte) 72, (byte) 99, (byte) 97, (byte) 26,
        (byte) 87, (byte) 187, (byte) 123, (byte) 46, (byte) 50, (byte) 90, (byte) (byte) 202, (byte) 117, (byte) 73,
        (byte) 10, (byte) 153, (byte) 47, (byte) 224, (byte) 178, (byte) 163, (byte) 77, (byte) 48, (byte) 46,
        (byte) (byte) 154, (byte) 33, (byte) 148, (byte) 34, (byte) 228, (byte) 33, (byte) 172, (byte) 216, (byte) 89,
        (byte) 46, (byte) 225, (byte) (byte) 127, (byte) 68, (byte) 146, (byte) 234, (byte) 30, (byte) 147, (byte) 54,
        (byte) 146, (byte) 5, (byte) 133, (byte) 45, (byte) 78, (byte) (byte) 254, (byte) 85, (byte) 55, (byte) 75,
        (byte) 213, (byte) 86, (byte) 194, (byte) 218, (byte) 215, (byte) 163, (byte) 189, (byte) (byte) 194,
        (byte) 54, (byte) 6, (byte) 83, (byte) 36, (byte) 18, (byte) 153, (byte) 53, (byte) 7, (byte) 48, (byte) 89,
        (byte) 35, (byte) 66, (byte) (byte) 144, (byte) 7, (byte) 65, (byte) 154, (byte) 13, (byte) 97, (byte) 75,
        (byte) 55, (byte) 230, (byte) 132, (byte) 3, (byte) 13, (byte) (byte) 239, (byte) 71 };
    final byte[] e = { 1, 0, 1 };
    final byte[] d = { (byte) 84, (byte) 80, (byte) 150, (byte) 58, (byte) 165, (byte) 235, (byte) 242, (byte) 123,
        (byte) 217, (byte) 55, (byte) 38, (byte) (byte) 154, (byte) 36, (byte) 181, (byte) 221, (byte) 156, (byte) 211,
        (byte) 215, (byte) 100, (byte) 164, (byte) 90, (byte) 88, (byte) (byte) 40, (byte) 228, (byte) 83, (byte) 148,
        (byte) 54, (byte) 122, (byte) 4, (byte) 16, (byte) 165, (byte) 48, (byte) 76, (byte) 194, (byte) (byte) 26,
        (byte) 107, (byte) 51, (byte) 53, (byte) 179, (byte) 165, (byte) 31, (byte) 18, (byte) 198, (byte) 173,
        (byte) 78, (byte) 61, (byte) (byte) 56, (byte) 97, (byte) 252, (byte) 158, (byte) 140, (byte) 80, (byte) 63,
        (byte) 25, (byte) 223, (byte) 156, (byte) 36, (byte) 203, (byte) (byte) 214, (byte) 252, (byte) 120, (byte) 67,
        (byte) 180, (byte) 167, (byte) 3, (byte) 82, (byte) 243, (byte) 25, (byte) 97, (byte) 214, (byte) (byte) 83,
        (byte) 133, (byte) 69, (byte) 16, (byte) 104, (byte) 54, (byte) 160, (byte) 200, (byte) 41, (byte) 83,
        (byte) 164, (byte) 187, (byte) (byte) 70, (byte) 153, (byte) 111, (byte) 234, (byte) 242, (byte) 158,
        (byte) 175, (byte) 28, (byte) 198, (byte) 48, (byte) 211, (byte) (byte) 45, (byte) 148, (byte) 58, (byte) 23,
        (byte) 62, (byte) 227, (byte) 74, (byte) 52, (byte) 117, (byte) 42, (byte) 90, (byte) 41, (byte) (byte) 249,
        (byte) 130, (byte) 154, (byte) 80, (byte) 119, (byte) 61, (byte) 26, (byte) 193, (byte) 40, (byte) 125,
        (byte) 10, (byte) (byte) 152, (byte) 174, (byte) 227, (byte) 225, (byte) 205, (byte) 32, (byte) 62, (byte) 66,
        (byte) 6, (byte) 163, (byte) 100, (byte) 99, (byte) (byte) 219, (byte) 19, (byte) 253, (byte) 25, (byte) 105,
        (byte) 80, (byte) 201, (byte) 29, (byte) 252, (byte) 157, (byte) 237, (byte) (byte) 69, (byte) 1, (byte) 80,
        (byte) 171, (byte) 167, (byte) 20, (byte) 196, (byte) 156, (byte) 109, (byte) 249, (byte) 88, (byte) 0,
        (byte) (byte) 3, (byte) 152, (byte) 38, (byte) 165, (byte) 72, (byte) 87, (byte) 6, (byte) 152, (byte) 71,
        (byte) 156, (byte) 214, (byte) 16, (byte) (byte) 71, (byte) 30, (byte) 82, (byte) 51, (byte) 103, (byte) 76,
        (byte) 218, (byte) 63, (byte) 9, (byte) 84, (byte) 163, (byte) 249, (byte) (byte) 91, (byte) 215, (byte) 44,
        (byte) 238, (byte) 85, (byte) 101, (byte) 240, (byte) 148, (byte) 1, (byte) 82, (byte) 224, (byte) 91,
        (byte) (byte) 135, (byte) 105, (byte) 127, (byte) 84, (byte) 171, (byte) 181, (byte) 152, (byte) 210,
        (byte) 183, (byte) 126, (byte) 24, (byte) (byte) 46, (byte) 196, (byte) 90, (byte) 173, (byte) 38, (byte) 245,
        (byte) 219, (byte) 186, (byte) 222, (byte) 27, (byte) 240, (byte) (byte) 212, (byte) 194, (byte) 15, (byte) 66,
        (byte) 135, (byte) 226, (byte) 178, (byte) 190, (byte) 52, (byte) 245, (byte) 74, (byte) (byte) 65, (byte) 224,
        (byte) 81, (byte) 100, (byte) 85, (byte) 25, (byte) 204, (byte) 165, (byte) 203, (byte) 187, (byte) 175,
        (byte) (byte) 84, (byte) 100, (byte) 82, (byte) 15, (byte) 11, (byte) 23, (byte) 202, (byte) 151, (byte) 107,
        (byte) 54, (byte) 41, (byte) 207, (byte) (byte) 3, (byte) 136, (byte) 229, (byte) 134, (byte) 131, (byte) 93,
        (byte) 139, (byte) 50, (byte) 182, (byte) 204, (byte) 93, (byte) (byte) 130, (byte) 89 };

    BigInteger N = new BigInteger(1, n);
    BigInteger E = new BigInteger(1, e);
    BigInteger D = new BigInteger(1, d);

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(N, E);
    RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(N, D);
    RSAPublicKey aRsaPublicKey = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec);
    RSAPrivateKey aRsaPrivKey = (RSAPrivateKey) keyFactory.generatePrivate(privKeySpec);

    SecretKey contentEncryptionKey = new SecretKeySpec(cmk, "AES");
    String encodedStr = "AxY8DCtDaGlsbGljb3RoZQ";
    String encrypted = JcBase.encrypt(plaintext.getBytes(), headerStr, aRsaPublicKey, contentEncryptionKey, encodedStr);
    String[] split = encrypted.split("\\.");
    assertEquals(5, split.length);

    byte[] cleartextBytes = JcBase.decrypt(encrypted, aRsaPrivKey);
    assertTrue(Arrays.equals(plaintextBytes, cleartextBytes));

    String ivStr = "AxY8DCtDaGlsbGljb3RoZQ";
    byte[] ivBytes = Base64.decodeUrl(ivStr);
    assertEquals(16, ivBytes.length);

    // String webToken =
    // "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDK0hTMjU2In0.Mw_3ejHF0lmjYXsPbJcATEAxvQugI7lZou6GMB_FrGE9rz5H9oOnKC4G3V9H6huoyDLyocNbHJiWYKncU1k4BYYnOQKCZPKKocPzreYEzZba_XiBceTQ3ltHJAyzWN46YGvrQ9SoW0Wsc-oYiZLz-m0Wu9HfFDJtEisLJN10mDwmN1ZzcjCHyH1SvTkZQxbPAxkRsJQXph8CuKsYnfi43aHm7g_NfSw8T88EQpN8lxEHeRsQaHzjzOdDXtIyKh9VFslN5Mz-7LhJio-Wqqrr8aG1X6dkc-r-vcN3yI67Ov-5AatzJIxarUajds0i30jqCViu7slQ-803ClYmZOdu5A.AxY8DCtDaGlsbGljb3RoZQ._Z_djlIoC4MDSCKireWS2beti4Q6iSG2UjFujQvdz-_PQdUcFNkOulegD6BgjgdFLjeB4HHOO7UHvP8PEDu0a0sA2a_-CI0w2YQQ2QQe35M.fE8IGdUsui9Z13-UdNC5--zvimuE3ojQSV28d6Z40Pk";
    //
    // byte[] x = JcBase.decrypt(webToken, aRsaPrivKey);
    // assertTrue(Arrays.equals(plaintextBytes, x));
  }

  public void testZip() throws Exception {
    String text = "JSON Web Encryption (JWE) is a means of representing encrypted "
        + "content using JavaScript Object Notation (JSON) data structures. "
        + "Cryptographic algorithms and identifiers for use with this "
        + "specification are described in the separate JSON Web Algorithms (JWA) "
        + "specification.  Related digital signature and MAC capabilities are "
        + "described in the separate JSON Web Signature (JWS) specification.";

    KeyGenerator keygen;
    try {
      keygen = KeyGenerator.getInstance("AES");
    } catch (NoSuchAlgorithmException e) {
      throw new CryptoException(e);
    }

    int keylength = 128;

    keygen.init(keylength);
    SecretKey key = keygen.generateKey();
    System.out.println("key: "
        + Base64.encodeBytes(key.getEncoded(), org.jsoncrypto.util.Base64.DONT_BREAK_LINES
            | org.jsoncrypto.util.Base64.URL));

    final String a128kw = "{\"zip\":\"DEF\", \"alg\":\"A128KW\", \"enc\":\"A128GCM\"}";
    // String encodedIv = "AxY8DCtDaGlsbGljb3RoZQ";
    String encrypted = JcBase.encrypt(text.getBytes(), a128kw, key, new SecureRandom());
    System.out.println("zipped: " + encrypted);
    String[] split = encrypted.split("\\.");

    assertEquals(5, split.length);
    assertEquals("eyJ6aXAiOiJERUYiLCAiYWxnIjoiQTEyOEtXIiwgImVuYyI6IkExMjhHQ00ifQ", split[0]);

    System.out.println("AES128 jwtCryptoSegment base64: " + split[1]);

    byte[] cleartext = JcBase.decrypt(encrypted, key);
    assertTrue(Arrays.equals(text.getBytes(), cleartext));

  }

  public void testGenerateCIK() throws Exception {
    byte[] CMK = { 4, (byte) 211, (byte) 31, (byte) 197, (byte) 84, (byte) 157, (byte) 252, (byte) 254, (byte) 11,
        (byte) 100, (byte) 157, (byte) 250, (byte) 63, (byte) 170, (byte) 106, (byte) 206, 107, (byte) 124, (byte) 212,
        (byte) 45, (byte) 111, (byte) 107, (byte) 9, (byte) 219, (byte) 200, (byte) 177, (byte) 0, (byte) 240,
        (byte) 143, (byte) 156, (byte) 44, (byte) 207 };
    byte[] CEK = { 37, (byte) 245, 125, (byte) 247, 113, (byte) 155, (byte) 238, 98, (byte) 228, (byte) 206, 62, 65,
        81, (byte) 153, 79, 91 };
    byte[] CIK = { (byte) 203, (byte) 194, (byte) 197, (byte) 180, (byte) 120, (byte) 46, (byte) 123, (byte) 202,
        (byte) 78, (byte) 12, (byte) 33, (byte) 116, (byte) 214, (byte) 247, (byte) 128, (byte) 41, (byte) 175,
        (byte) 53, (byte) 181, (byte) 164, (byte) 224, (byte) 223, (byte) 56, (byte) 146, (byte) 179, (byte) 193,
        (byte) 18, (byte) 223, (byte) 146, (byte) 85, (byte) 244, (byte) 127 };

    String encStr = "A128CBC+HS256";
    byte[] cik = JcBase.generateCIK(CMK, CIK.length, new SHA256Digest(), encStr);
    assertTrue(Arrays.equals(CIK, cik));

    byte[] cek = JcBase.generateCEK(CMK, CEK.length, new SHA256Digest(), encStr);
    assertTrue(Arrays.equals(CEK, cek));
  }

  public void testRSA15_201208() throws JcBaseException, Exception {
    byte[] CMK = { 4, (byte) 211, (byte) 31, (byte) 197, (byte) 84, (byte) 157, (byte) 252, (byte) 254, (byte) 11,
        (byte) 100, (byte) 157, (byte) 250, (byte) 63, (byte) 170, (byte) 106, (byte) 206, 107, (byte) 124, (byte) 212,
        (byte) 45, (byte) 111, (byte) 107, (byte) 9, (byte) 219, (byte) 200, (byte) 177, (byte) 0, (byte) 240,
        (byte) 143, (byte) 156, (byte) 44, (byte) 207 };

    byte[] Plaintext = { 78, 111, 32, 109, 97, 116, 116, 101, 114, 32, 119, 104, 101, 114, 101, 32, 121, 111, 117, 32,
        103, 111, 44, 32, 116, 104, 101, 114, 101, 32, 121, 111, 117, 32, 97, 114, 101, 46 };
    String Header = "{\"alg\":\"RSA1_5\",\"enc\":\"A128CBC+HS256\"}";
    byte[] HeaderBytes = { 123, 34, 97, 108, 103, 34, 58, 34, 82, 83, 65, 49, 95, 53, 34, 44, 34, 101, 110, 99, 34, 58,
        34, 65, 49, 50, 56, 67, 66, 67, 43, 72, 83, 50, 53, 54, 34, 125 };
    assertTrue(Arrays.equals(HeaderBytes, Header.getBytes()));
    String EncodedHeader = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDK0hTMjU2In0";
    assertEquals(EncodedHeader,
        Base64.encodeBytes(HeaderBytes, org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL));

    String EncodedIV = "AxY8DCtDaGlsbGljb3RoZQ";
    byte[] IV = { 3, 22, 60, 12, 43, 67, 104, 105, 108, 108, 105, 99, 111, 116, 104, 101 };
    assertEquals(EncodedIV,
        Base64.encodeBytes(IV, org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL));
    // Exponent
    byte[] e = { 1, 0, 1 };
    // Modulus
    byte[] n = { (byte) 177, (byte) 119, (byte) 33, (byte) 13, (byte) 164, (byte) 30, (byte) 108, (byte) 121,
        (byte) 207, (byte) 136, (byte) 107, (byte) 242, (byte) 12, (byte) 224, (byte) 19, (byte) 226, (byte) 198,
        (byte) 134, (byte) 17, (byte) 71, (byte) 173, (byte) 75, (byte) 42, (byte) 61, (byte) 48, (byte) 162,
        (byte) 206, (byte) 161, (byte) 97, (byte) 108, (byte) 185, (byte) 234, (byte) 226, (byte) 219, (byte) 118,
        (byte) 206, (byte) 118, (byte) 5, (byte) 169, (byte) 224, (byte) 60, (byte) 181, (byte) 90, (byte) 85,
        (byte) 51, (byte) 123, (byte) 6, (byte) 224, 4, (byte) 122, (byte) 29, (byte) 230, (byte) 151, (byte) 12,
        (byte) 244, (byte) 127, (byte) 121, (byte) 25, (byte) 4, (byte) 85, (byte) 220, (byte) 144, (byte) 215,
        (byte) 110, (byte) 130, (byte) 17, (byte) 68, (byte) 228, (byte) 129, (byte) 138, (byte) 7, (byte) 130,
        (byte) 231, (byte) 40, (byte) 212, (byte) 214, (byte) 17, (byte) 179, (byte) 28, (byte) 124, (byte) 151,
        (byte) 178, (byte) 207, (byte) 20, (byte) 14, (byte) 154, (byte) 222, (byte) 113, (byte) 176, (byte) 24,
        (byte) 198, (byte) 73, (byte) 211, (byte) 113, (byte) 9, (byte) 33, (byte) 178, (byte) 80, (byte) 13,
        (byte) 25, (byte) 21, (byte) 25, (byte) 153, (byte) 212, (byte) 206, (byte) 67, (byte) 154, (byte) 147,
        (byte) 70, (byte) 194, (byte) 192, (byte) 183, (byte) 160, (byte) 83, (byte) 98, (byte) 236, (byte) 175,
        (byte) 85, (byte) 23, (byte) 97, (byte) 75, (byte) 199, (byte) 177, (byte) 73, (byte) 145, (byte) 50,
        (byte) 253, (byte) 206, 32, (byte) 179, (byte) 254, (byte) 236, (byte) 190, (byte) 82, (byte) 73, (byte) 67,
        (byte) 129, (byte) 253, (byte) 252, (byte) 220, (byte) 108, (byte) 136, (byte) 138, (byte) 11, (byte) 192,
        (byte) 1, (byte) 36, (byte) 239, (byte) 228, (byte) 55, (byte) 81, (byte) 113, (byte) 17, (byte) 25,
        (byte) 140, (byte) 63, (byte) 239, (byte) 146, (byte) 3, (byte) 172, 96, (byte) 60, (byte) 227, (byte) 233,
        (byte) 64, (byte) 255, (byte) 224, (byte) 173, (byte) 225, (byte) 228, (byte) 229, (byte) 92, (byte) 112,
        (byte) 72, (byte) 99, (byte) 97, 26, (byte) 87, (byte) 187, (byte) 123, (byte) 46, (byte) 50, (byte) 90,
        (byte) 202, (byte) 117, (byte) 73, (byte) 10, (byte) 153, (byte) 47, (byte) 224, (byte) 178, (byte) 163, 77,
        (byte) 48, (byte) 46, (byte) 154, (byte) 33, (byte) 148, (byte) 34, (byte) 228, (byte) 33, (byte) 172,
        (byte) 216, (byte) 89, (byte) 46, (byte) 225, (byte) 127, (byte) 68, (byte) 146, (byte) 234, (byte) 30,
        (byte) 147, (byte) 54, (byte) 146, (byte) 5, (byte) 133, (byte) 45, (byte) 78, (byte) 254, (byte) 85,
        (byte) 55, (byte) 75, (byte) 213, (byte) 86, (byte) 194, (byte) 218, (byte) 215, (byte) 163, (byte) 189,
        (byte) 194, (byte) 54, (byte) 6, (byte) 83, (byte) 36, (byte) 18, (byte) 153, (byte) 53, (byte) 7, (byte) 48,
        (byte) 89, 35, (byte) 66, (byte) 144, (byte) 7, (byte) 65, (byte) 154, (byte) 13, (byte) 97, (byte) 75,
        (byte) 55, (byte) 230, (byte) 132, (byte) 3, (byte) 13, (byte) 239, (byte) 71 };
    // D (private key exponent)
    byte[] d = { 84, (byte) 80, (byte) 150, (byte) 58, (byte) 165, (byte) 235, (byte) 242, (byte) 123, (byte) 217,
        (byte) 55, (byte) 38, (byte) 154, (byte) 36, (byte) 181, (byte) 221, (byte) 156, (byte) 211, (byte) 215,
        (byte) 100, (byte) 164, (byte) 90, (byte) 88, (byte) 40, (byte) 228, (byte) 83, (byte) 148, (byte) 54,
        (byte) 122, (byte) 4, (byte) 16, (byte) 165, (byte) 48, 76, (byte) 194, (byte) 26, (byte) 107, (byte) 51,
        (byte) 53, (byte) 179, (byte) 165, (byte) 31, (byte) 18, (byte) 198, (byte) 173, (byte) 78, (byte) 61,
        (byte) 56, (byte) 97, (byte) 252, (byte) 158, (byte) 140, (byte) 80, (byte) 63, (byte) 25, (byte) 223,
        (byte) 156, (byte) 36, (byte) 203, (byte) 214, (byte) 252, (byte) 120, (byte) 67, (byte) 180, (byte) 167, 3,
        (byte) 82, (byte) 243, (byte) 25, (byte) 97, (byte) 214, (byte) 83, (byte) 133, (byte) 69, (byte) 16,
        (byte) 104, (byte) 54, (byte) 160, (byte) 200, (byte) 41, (byte) 83, (byte) 164, (byte) 187, (byte) 70,
        (byte) 153, (byte) 111, (byte) 234, (byte) 242, (byte) 158, (byte) 175, (byte) 28, (byte) 198, (byte) 48,
        (byte) 211, (byte) 45, (byte) 148, (byte) 58, 23, (byte) 62, (byte) 227, (byte) 74, (byte) 52, (byte) 117,
        (byte) 42, (byte) 90, (byte) 41, (byte) 249, (byte) 130, (byte) 154, (byte) 80, (byte) 119, (byte) 61,
        (byte) 26, (byte) 193, (byte) 40, (byte) 125, (byte) 10, (byte) 152, (byte) 174, (byte) 227, (byte) 225,
        (byte) 205, (byte) 32, (byte) 62, (byte) 66, (byte) 6, (byte) 163, (byte) 100, (byte) 99, (byte) 219,
        (byte) 19, (byte) 253, (byte) 25, (byte) 105, (byte) 80, (byte) 201, (byte) 29, (byte) 252, (byte) 157,
        (byte) 237, (byte) 69, (byte) 1, (byte) 80, (byte) 171, (byte) 167, 20, (byte) 196, (byte) 156, (byte) 109,
        (byte) 249, (byte) 88, (byte) 0, (byte) 3, (byte) 152, (byte) 38, (byte) 165, (byte) 72, (byte) 87, (byte) 6,
        (byte) 152, (byte) 71, (byte) 156, (byte) 214, (byte) 16, (byte) 71, (byte) 30, (byte) 82, (byte) 51,
        (byte) 103, (byte) 76, (byte) 218, (byte) 63, (byte) 9, (byte) 84, (byte) 163, (byte) 249, (byte) 91,
        (byte) 215, (byte) 44, (byte) 238, (byte) 85, (byte) 101, (byte) 240, (byte) 148, (byte) 1, (byte) 82,
        (byte) 224, (byte) 91, (byte) 135, (byte) 105, (byte) 127, (byte) 84, (byte) 171, (byte) 181, (byte) 152,
        (byte) 210, (byte) 183, (byte) 126, (byte) 24, (byte) 46, (byte) 196, (byte) 90, (byte) 173, (byte) 38,
        (byte) 245, (byte) 219, (byte) 186, (byte) 222, (byte) 27, (byte) 240, (byte) 212, (byte) 194, (byte) 15,
        (byte) 66, (byte) 135, (byte) 226, (byte) 178, (byte) 190, (byte) 52, (byte) 245, (byte) 74, (byte) 65,
        (byte) 224, (byte) 81, (byte) 100, 85, (byte) 25, (byte) 204, (byte) 165, (byte) 203, (byte) 187, (byte) 175,
        (byte) 84, (byte) 100, (byte) 82, (byte) 15, (byte) 11, (byte) 23, (byte) 202, (byte) 151, (byte) 107, 54,
        (byte) 41, (byte) 207, (byte) 3, (byte) 136, (byte) 229, (byte) 134, (byte) 131, (byte) 93, (byte) 139,
        (byte) 50, (byte) 182, (byte) 204, (byte) 93, (byte) 130, (byte) 89 };

    BigInteger N = new BigInteger(1, n);
    BigInteger E = new BigInteger(1, e);
    BigInteger D = new BigInteger(1, d);

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(N, E);
    RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(N, D);
    RSAPublicKey aRsaPublicKey = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec);
    RSAPrivateKey aRsaPrivKey = (RSAPrivateKey) keyFactory.generatePrivate(privKeySpec);

    byte[] EncryptedKey = { 59, (byte) 160, (byte) 42, (byte) 94, (byte) 168, (byte) 21, (byte) 148, (byte) 146,
        (byte) 120, (byte) 115, (byte) 137, (byte) 105, (byte) 230, (byte) 197, (byte) 217, (byte) 119, (byte) 182,
        (byte) 233, (byte) 24, (byte) 112, (byte) 48, (byte) 232, (byte) 4, (byte) 100, (byte) 29, (byte) 71,
        (byte) 151, (byte) 67, (byte) 16, (byte) 245, (byte) 112, (byte) 5, (byte) 184, (byte) 249, (byte) 125,
        (byte) 113, (byte) 210, (byte) 171, (byte) 99, (byte) 252, (byte) 3, (byte) 116, (byte) 154, (byte) 233,
        (byte) 42, (byte) 16, (byte) 233, (byte) 120, 99, (byte) 165, (byte) 14, (byte) 192, (byte) 149, (byte) 200,
        (byte) 37, (byte) 143, (byte) 126, (byte) 27, (byte) 81, (byte) 11, (byte) 43, (byte) 228, (byte) 80,
        (byte) 90, (byte) 178, (byte) 135, (byte) 253, (byte) 43, (byte) 81, (byte) 248, (byte) 3, (byte) 61,
        (byte) 31, (byte) 177, (byte) 169, (byte) 42, (byte) 100, (byte) 132, (byte) 142, (byte) 47, 16, (byte) 96,
        (byte) 231, (byte) 12, (byte) 58, (byte) 58, (byte) 254, (byte) 187, (byte) 209, (byte) 245, (byte) 46,
        (byte) 223, (byte) 233, (byte) 4, (byte) 72, (byte) 158, 27, (byte) 205, (byte) 54, (byte) 75, (byte) 21,
        (byte) 179, (byte) 210, (byte) 182, (byte) 197, (byte) 77, (byte) 116, (byte) 92, (byte) 143, (byte) 128,
        (byte) 86, (byte) 47, 23, (byte) 41, (byte) 10, (byte) 216, (byte) 212, (byte) 103, (byte) 125, (byte) 64,
        (byte) 63, (byte) 235, (byte) 28, (byte) 248, (byte) 191, (byte) 222, (byte) 9, (byte) 158, 84, (byte) 54,
        (byte) 93, (byte) 26, (byte) 73, (byte) 19, (byte) 106, (byte) 22, (byte) 201, (byte) 46, (byte) 62, (byte) 76,
        (byte) 209, (byte) 176, (byte) 86, (byte) 81, 12, (byte) 154, (byte) 146, (byte) 72, (byte) 151, (byte) 149,
        (byte) 59, (byte) 34, (byte) 123, (byte) 165, (byte) 144, (byte) 144, (byte) 243, (byte) 119, (byte) 229,
        (byte) 139, 95, (byte) 85, (byte) 130, (byte) 47, (byte) 247, (byte) 71, (byte) 181, (byte) 204, (byte) 82,
        (byte) 169, (byte) 110, (byte) 66, (byte) 235, (byte) 156, (byte) 237, (byte) 145, (byte) 206, (byte) 90,
        (byte) 44, (byte) 98, (byte) 227, (byte) 184, (byte) 215, (byte) 148, (byte) 20, (byte) 229, (byte) 89,
        (byte) 22, (byte) 121, (byte) 136, (byte) 92, (byte) 90, 75, (byte) 193, (byte) 186, (byte) 39, (byte) 204,
        (byte) 20, (byte) 173, (byte) 222, (byte) 1, (byte) 42, (byte) 8, (byte) 0, (byte) 195, (byte) 122, (byte) 151,
        (byte) 229, 122, (byte) 8, (byte) 125, (byte) 116, (byte) 21, (byte) 149, (byte) 6, (byte) 68, (byte) 209,
        (byte) 89, (byte) 164, (byte) 171, (byte) 252, (byte) 208, (byte) 72, (byte) 138, 57, (byte) 46, (byte) 113,
        (byte) 74, (byte) 202, (byte) 140, (byte) 40, (byte) 161, (byte) 23, (byte) 29, (byte) 176, (byte) 75,
        (byte) 141, (byte) 135, (byte) 208, (byte) 125, 50, (byte) 76, (byte) 173, (byte) 209, (byte) 148, (byte) 4,
        (byte) 148, (byte) 111, (byte) 163, (byte) 25, (byte) 219, (byte) 71, (byte) 90, (byte) 54, (byte) 82,
        (byte) 56 };
    String EncodedEncryptedKey = "O6AqXqgVlJJ4c4lp5sXZd7bpGHAw6ARkHUeXQxD1cAW4-X1x0qtj_AN0mukqEOl4Y6UOwJXIJY9-G1ELK-RQWrKH_StR-AM9H7GpKmSEji8QYOcMOjr-u9H1Lt_pBEieG802SxWz0rbFTXRcj4BWLxcpCtjUZ31AP-sc-L_eCZ5UNl0aSRNqFskuPkzRsFZRDJqSSJeVOyJ7pZCQ83fli19Vgi_3R7XMUqluQuuc7ZHOWixi47jXlBTlWRZ5iFxaS8G6J8wUrd4BKggAw3qX5XoIfXQVlQZE0Vmkq_zQSIo5LnFKyowooRcdsEuNh9B9Mkyt0ZQElG-jGdtHWjZSOA";
    assertEquals(EncodedEncryptedKey,
        Base64.encodeBytes(EncryptedKey, org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL));

    SecretKey secretKey = new SecretKeySpec(CMK, "AES");
    // String encodedEncryptedKey =
    // JcBase.generateEncodedEncryptedKeyRSA15(aRsaPublicKey, secretKey);
    // randomness assertEquals(EncodedEncryptedKey, encodedEncryptedKey);

    String encrypted = JcBase.encrypt(Plaintext, Header, aRsaPublicKey, secretKey, EncodedIV);
    String[] split = encrypted.split("\\.");

    assertEquals(5, split.length);

    String newJwtHeaderSegment = new String(Base64.decodeUrl(split[0]));
    JSONObject newJwtHeader = new JSONObject(newJwtHeaderSegment);
    JSONObject oldJwtHeader = new JSONObject(Header);

    assertEquals(oldJwtHeader.get("alg"), newJwtHeader.get("alg"));
    assertEquals(oldJwtHeader.get("enc"), newJwtHeader.get("enc"));
    System.out.println("testRSA15_201208" + " jwtSymmetricKeySegment base64: " + split[1]);
    System.out.println("testRSA15_201208" + " jwtIvSegment base64: " + split[2]);
    System.out.println("testRSA15_201208" + " jwtCryptoSegment base64: " + split[3]);

    byte[] cleartextBytes = JcBase.decrypt(encrypted, aRsaPrivKey);
    assertTrue(Arrays.equals(Plaintext, cleartextBytes));
  }

  public void testRSA15_201208decrypt() throws Exception {
    // Modulus
    byte[] n = { (byte) 177, (byte) 119, (byte) 33, (byte) 13, (byte) 164, (byte) 30, (byte) 108, (byte) 121,
        (byte) 207, (byte) 136, (byte) 107, (byte) 242, (byte) 12, (byte) 224, (byte) 19, (byte) 226, (byte) 198,
        (byte) 134, (byte) 17, (byte) 71, (byte) 173, (byte) 75, (byte) 42, (byte) 61, (byte) 48, (byte) 162,
        (byte) 206, (byte) 161, (byte) 97, (byte) 108, (byte) 185, (byte) 234, (byte) 226, (byte) 219, (byte) 118,
        (byte) 206, (byte) 118, (byte) 5, (byte) 169, (byte) 224, (byte) 60, (byte) 181, (byte) 90, (byte) 85,
        (byte) 51, (byte) 123, (byte) 6, (byte) 224, 4, (byte) 122, (byte) 29, (byte) 230, (byte) 151, (byte) 12,
        (byte) 244, (byte) 127, (byte) 121, (byte) 25, (byte) 4, (byte) 85, (byte) 220, (byte) 144, (byte) 215,
        (byte) 110, (byte) 130, (byte) 17, (byte) 68, (byte) 228, (byte) 129, (byte) 138, (byte) 7, (byte) 130,
        (byte) 231, (byte) 40, (byte) 212, (byte) 214, (byte) 17, (byte) 179, (byte) 28, (byte) 124, (byte) 151,
        (byte) 178, (byte) 207, (byte) 20, (byte) 14, (byte) 154, (byte) 222, (byte) 113, (byte) 176, (byte) 24,
        (byte) 198, (byte) 73, (byte) 211, (byte) 113, (byte) 9, (byte) 33, (byte) 178, (byte) 80, (byte) 13,
        (byte) 25, (byte) 21, (byte) 25, (byte) 153, (byte) 212, (byte) 206, (byte) 67, (byte) 154, (byte) 147,
        (byte) 70, (byte) 194, (byte) 192, (byte) 183, (byte) 160, (byte) 83, (byte) 98, (byte) 236, (byte) 175,
        (byte) 85, (byte) 23, (byte) 97, (byte) 75, (byte) 199, (byte) 177, (byte) 73, (byte) 145, (byte) 50,
        (byte) 253, (byte) 206, 32, (byte) 179, (byte) 254, (byte) 236, (byte) 190, (byte) 82, (byte) 73, (byte) 67,
        (byte) 129, (byte) 253, (byte) 252, (byte) 220, (byte) 108, (byte) 136, (byte) 138, (byte) 11, (byte) 192,
        (byte) 1, (byte) 36, (byte) 239, (byte) 228, (byte) 55, (byte) 81, (byte) 113, (byte) 17, (byte) 25,
        (byte) 140, (byte) 63, (byte) 239, (byte) 146, (byte) 3, (byte) 172, 96, (byte) 60, (byte) 227, (byte) 233,
        (byte) 64, (byte) 255, (byte) 224, (byte) 173, (byte) 225, (byte) 228, (byte) 229, (byte) 92, (byte) 112,
        (byte) 72, (byte) 99, (byte) 97, 26, (byte) 87, (byte) 187, (byte) 123, (byte) 46, (byte) 50, (byte) 90,
        (byte) 202, (byte) 117, (byte) 73, (byte) 10, (byte) 153, (byte) 47, (byte) 224, (byte) 178, (byte) 163, 77,
        (byte) 48, (byte) 46, (byte) 154, (byte) 33, (byte) 148, (byte) 34, (byte) 228, (byte) 33, (byte) 172,
        (byte) 216, (byte) 89, (byte) 46, (byte) 225, (byte) 127, (byte) 68, (byte) 146, (byte) 234, (byte) 30,
        (byte) 147, (byte) 54, (byte) 146, (byte) 5, (byte) 133, (byte) 45, (byte) 78, (byte) 254, (byte) 85,
        (byte) 55, (byte) 75, (byte) 213, (byte) 86, (byte) 194, (byte) 218, (byte) 215, (byte) 163, (byte) 189,
        (byte) 194, (byte) 54, (byte) 6, (byte) 83, (byte) 36, (byte) 18, (byte) 153, (byte) 53, (byte) 7, (byte) 48,
        (byte) 89, 35, (byte) 66, (byte) 144, (byte) 7, (byte) 65, (byte) 154, (byte) 13, (byte) 97, (byte) 75,
        (byte) 55, (byte) 230, (byte) 132, (byte) 3, (byte) 13, (byte) 239, (byte) 71 };
    // D (private key exponent)
    byte[] d = { 84, (byte) 80, (byte) 150, (byte) 58, (byte) 165, (byte) 235, (byte) 242, (byte) 123, (byte) 217,
        (byte) 55, (byte) 38, (byte) 154, (byte) 36, (byte) 181, (byte) 221, (byte) 156, (byte) 211, (byte) 215,
        (byte) 100, (byte) 164, (byte) 90, (byte) 88, (byte) 40, (byte) 228, (byte) 83, (byte) 148, (byte) 54,
        (byte) 122, (byte) 4, (byte) 16, (byte) 165, (byte) 48, 76, (byte) 194, (byte) 26, (byte) 107, (byte) 51,
        (byte) 53, (byte) 179, (byte) 165, (byte) 31, (byte) 18, (byte) 198, (byte) 173, (byte) 78, (byte) 61,
        (byte) 56, (byte) 97, (byte) 252, (byte) 158, (byte) 140, (byte) 80, (byte) 63, (byte) 25, (byte) 223,
        (byte) 156, (byte) 36, (byte) 203, (byte) 214, (byte) 252, (byte) 120, (byte) 67, (byte) 180, (byte) 167, 3,
        (byte) 82, (byte) 243, (byte) 25, (byte) 97, (byte) 214, (byte) 83, (byte) 133, (byte) 69, (byte) 16,
        (byte) 104, (byte) 54, (byte) 160, (byte) 200, (byte) 41, (byte) 83, (byte) 164, (byte) 187, (byte) 70,
        (byte) 153, (byte) 111, (byte) 234, (byte) 242, (byte) 158, (byte) 175, (byte) 28, (byte) 198, (byte) 48,
        (byte) 211, (byte) 45, (byte) 148, (byte) 58, 23, (byte) 62, (byte) 227, (byte) 74, (byte) 52, (byte) 117,
        (byte) 42, (byte) 90, (byte) 41, (byte) 249, (byte) 130, (byte) 154, (byte) 80, (byte) 119, (byte) 61,
        (byte) 26, (byte) 193, (byte) 40, (byte) 125, (byte) 10, (byte) 152, (byte) 174, (byte) 227, (byte) 225,
        (byte) 205, (byte) 32, (byte) 62, (byte) 66, (byte) 6, (byte) 163, (byte) 100, (byte) 99, (byte) 219,
        (byte) 19, (byte) 253, (byte) 25, (byte) 105, (byte) 80, (byte) 201, (byte) 29, (byte) 252, (byte) 157,
        (byte) 237, (byte) 69, (byte) 1, (byte) 80, (byte) 171, (byte) 167, 20, (byte) 196, (byte) 156, (byte) 109,
        (byte) 249, (byte) 88, (byte) 0, (byte) 3, (byte) 152, (byte) 38, (byte) 165, (byte) 72, (byte) 87, (byte) 6,
        (byte) 152, (byte) 71, (byte) 156, (byte) 214, (byte) 16, (byte) 71, (byte) 30, (byte) 82, (byte) 51,
        (byte) 103, (byte) 76, (byte) 218, (byte) 63, (byte) 9, (byte) 84, (byte) 163, (byte) 249, (byte) 91,
        (byte) 215, (byte) 44, (byte) 238, (byte) 85, (byte) 101, (byte) 240, (byte) 148, (byte) 1, (byte) 82,
        (byte) 224, (byte) 91, (byte) 135, (byte) 105, (byte) 127, (byte) 84, (byte) 171, (byte) 181, (byte) 152,
        (byte) 210, (byte) 183, (byte) 126, (byte) 24, (byte) 46, (byte) 196, (byte) 90, (byte) 173, (byte) 38,
        (byte) 245, (byte) 219, (byte) 186, (byte) 222, (byte) 27, (byte) 240, (byte) 212, (byte) 194, (byte) 15,
        (byte) 66, (byte) 135, (byte) 226, (byte) 178, (byte) 190, (byte) 52, (byte) 245, (byte) 74, (byte) 65,
        (byte) 224, (byte) 81, (byte) 100, 85, (byte) 25, (byte) 204, (byte) 165, (byte) 203, (byte) 187, (byte) 175,
        (byte) 84, (byte) 100, (byte) 82, (byte) 15, (byte) 11, (byte) 23, (byte) 202, (byte) 151, (byte) 107, 54,
        (byte) 41, (byte) 207, (byte) 3, (byte) 136, (byte) 229, (byte) 134, (byte) 131, (byte) 93, (byte) 139,
        (byte) 50, (byte) 182, (byte) 204, (byte) 93, (byte) 130, (byte) 89 };

    BigInteger N = new BigInteger(1, n);
    BigInteger D = new BigInteger(1, d);

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(N, D);
    RSAPrivateKey aRsaPrivKey = (RSAPrivateKey) keyFactory.generatePrivate(privKeySpec);

    String jwe = "eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDK0hTMjU2In0.O6AqXqgVlJJ4c4lp5sXZd7bpGHAw6ARkHUeXQxD1cAW4-X1x0qtj_AN0mukqEOl4Y6UOwJXIJY9-G1ELK-RQWrKH_StR-AM9H7GpKmSEji8QYOcMOjr-u9H1Lt_pBEieG802SxWz0rbFTXRcj4BWLxcpCtjUZ31AP-sc-L_eCZ5UNl0aSRNqFskuPkzRsFZRDJqSSJeVOyJ7pZCQ83fli19Vgi_3R7XMUqluQuuc7ZHOWixi47jXlBTlWRZ5iFxaS8G6J8wUrd4BKggAw3qX5XoIfXQVlQZE0Vmkq_zQSIo5LnFKyowooRcdsEuNh9B9Mkyt0ZQElG-jGdtHWjZSOA.AxY8DCtDaGlsbGljb3RoZQ.1eBWFgcrz40wC88cgv8rPgu3EfmC1p4zT0kIxxfSF2zDJcQ-iEHk1jQM95xAdr5Z.RBGhYzE8_cZLHjJqqHuLhzbgWgL_wV3LDSUrcbkOiIA";

    String[] split = jwe.split("\\.");
    if (split.length != 5) {
      throw new JcBaseException("jwt format exception #" + split.length);
    }

    String encodedJwtHeaderSegment = split[0];
    String encodedJwtKeySegment = split[1];
    String encodedJwtIvSegment = split[2];
    String encodedJwtCryptoSegment = split[3];
    String encodedJwtIntegritySegment = split[4];
    byte[] jwtIntegritySegmentBytes = Base64.decodeUrl(encodedJwtIntegritySegment);
    String jwtHeaderStr = new String(Base64.decodeUrl(encodedJwtHeaderSegment));
    JSONObject header = new JSONObject(jwtHeaderStr);

    byte[] IntegrityValue = { 68, (byte) 17, (byte) 161, (byte) 99, (byte) 49, (byte) 60, (byte) 253, (byte) 198,
        (byte) 75, (byte) 30, (byte) 50, (byte) 106, (byte) 168, (byte) 123, (byte) 139, (byte) 135, 54, (byte) 224,
        (byte) 90, (byte) 2, (byte) 255, (byte) 193, (byte) 93, (byte) 203, (byte) 13, (byte) 37, (byte) 43,
        (byte) 113, (byte) 185, (byte) 14, (byte) 136, (byte) 128 };
    String EncodedIntegrityValue = "RBGhYzE8_cZLHjJqqHuLhzbgWgL_wV3LDSUrcbkOiIA";
    assertEquals(EncodedIntegrityValue, Base64.encodeBytes(IntegrityValue, org.jsoncrypto.util.Base64.DONT_BREAK_LINES
        | org.jsoncrypto.util.Base64.URL));
    assertTrue(Arrays.equals(IntegrityValue, jwtIntegritySegmentBytes));

    String newKeySegment = split[1];
    byte[] cipheredKeyBytes = Base64.decodeUrl(newKeySegment);

    String jwtAlgStr = header.getString("alg");
    String jwtEncStr = header.getString("enc");
    assertEquals("RSA1_5", jwtAlgStr);
    assertEquals("A128CBC+HS256", jwtEncStr);

    byte[] secretKeyBytes = JcBase.decryptKey(aRsaPrivKey, jwtAlgStr, cipheredKeyBytes);
    byte[] CMK = { 4, (byte) 211, (byte) 31, (byte) 197, (byte) 84, (byte) 157, (byte) 252, (byte) 254, (byte) 11,
        (byte) 100, (byte) 157, (byte) 250, (byte) 63, (byte) 170, (byte) 106, (byte) 206, 107, (byte) 124, (byte) 212,
        (byte) 45, (byte) 111, (byte) 107, (byte) 9, (byte) 219, (byte) 200, (byte) 177, (byte) 0, (byte) 240,
        (byte) 143, (byte) 156, (byte) 44, (byte) 207 };
    assertTrue(Arrays.equals(secretKeyBytes, CMK));

    byte[] Plaintext = { 78, 111, 32, 109, 97, 116, 116, 101, 114, 32, 119, 104, 101, 114, 101, 32, 121, 111, 117, 32,
        103, 111, 44, 32, 116, 104, 101, 114, 101, 32, 121, 111, 117, 32, 97, 114, 101, 46 };

    {
      int cekLength = JcBase.enc2cekLength(header.getString("enc"));
      Digest kdfDigest = new SHA256Digest();
      byte[] cek = JcBase.generateCEK(secretKeyBytes, cekLength, kdfDigest, jwtEncStr);

      Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
      String ivStr = encodedJwtIvSegment;
      byte[] ivBytes = Base64.decodeUrl(ivStr);
      IvParameterSpec ivParameter = new IvParameterSpec(ivBytes);
      cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(cek, "AES"), ivParameter);
      byte[] jwtCrytoSegmentBytes = Base64.decodeUrl(encodedJwtCryptoSegment);
      byte[] cleartextBytes = cipher.doFinal(jwtCrytoSegmentBytes);
      assertTrue(Arrays.equals(Plaintext, cleartextBytes));

      String jwtIntStr = JcBase.getIntAlg(header);
      Mac mac = JcBase.getMac(jwtIntStr);
      int cikLength = mac.getMacLength();
      byte[] cik = JcBase.generateCIK(secretKeyBytes, cikLength, kdfDigest, jwtEncStr);
      byte[] CIK = { (byte) 203, (byte) 194, (byte) 197, (byte) 180, (byte) 120, (byte) 46, (byte) 123, (byte) 202,
          (byte) 78, (byte) 12, (byte) 33, (byte) 116, (byte) 214, (byte) 247, (byte) 128, (byte) 41, (byte) 175,
          (byte) 53, (byte) 181, (byte) 164, (byte) 224, (byte) 223, (byte) 56, (byte) 146, (byte) 179, (byte) 193,
          (byte) 18, (byte) 223, (byte) 146, (byte) 85, (byte) 244, (byte) 127 };
      assertTrue(Arrays.equals(cik, CIK));
      String stringToSign = encodedJwtHeaderSegment + "." + encodedJwtKeySegment + "." + encodedJwtIvSegment + "."
          + encodedJwtCryptoSegment;
      byte[] bytes = JcBase.doMac(mac, cik, stringToSign.getBytes());

      if (!org.bouncycastle.util.Arrays.constantTimeAreEqual(bytes, jwtIntegritySegmentBytes)) {
        throw new JcBaseException("integrity check failed");
      }
    }
    byte[] ctBytes = JcBase.doJwtDecrypt(encodedJwtHeaderSegment, encodedJwtKeySegment, encodedJwtCryptoSegment,
        jwtIntegritySegmentBytes, header, secretKeyBytes, encodedJwtIvSegment);
    assertTrue(Arrays.equals(Plaintext, ctBytes));

    byte[] cleartextBytes = JcBase.decrypt(jwe, aRsaPrivKey);
    assertTrue(Arrays.equals(Plaintext, cleartextBytes));
  }

  public void testJWE2log_201208() throws Exception {
    byte[] CMK = { (byte) 177, (byte) 161, (byte) 244, (byte) 128, (byte) 84, (byte) 143, (byte) 225, (byte) 115,
        (byte) 63, (byte) 180, (byte) 3, (byte) 255, (byte) 107, (byte) 154, (byte) 212, (byte) 246, (byte) 138,
        (byte) 7, (byte) 110, (byte) 91, (byte) 112, (byte) 46, (byte) 34, (byte) 105, (byte) 47, (byte) 130,
        (byte) 203, (byte) 46, (byte) 122, (byte) 234, (byte) 64, (byte) 252 };
    // byte[] IV = { (byte) 227, (byte) 197, (byte) 117, (byte) 252, (byte) 2,
    // (byte) 219, (byte) 233, (byte) 68,
    // (byte) 180, (byte) 225, (byte) 77, (byte) 219 };
    byte[] Plaintext = { 76, 105, 118, 101, 32, 108, 111, 110, 103, 32, 97, 110, 100, 32, 112, 114, 111, 115, 112, 101,
        114, 46 };
    String Header = "{\"alg\":\"RSA-OAEP\",\"enc\":\"A256GCM\"}";
    byte[] HeaderBytes = { 123, 34, 97, 108, 103, 34, 58, 34, 82, 83, 65, 45, 79, 65, 69, 80, 34, 44, 34, 101, 110, 99,
        34, 58, 34, 65, 50, 53, 54, 71, 67, 77, 34, 125 };
    String EncodedHeader = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ";
    assertEquals(EncodedHeader,
        Base64.encodeBytes(HeaderBytes, org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL));

    String EncodedIV = "48V1_ALb6US04U3b";

    // Exponent
    byte[] e = { 1, 0, 1 };
    // Modulus
    byte[] n = { (byte) 161, (byte) 168, (byte) 84, (byte) 34, (byte) 133, (byte) 176, (byte) 208, (byte) 173,
        (byte) 46, (byte) 176, (byte) 163, (byte) 110, (byte) 57, (byte) 30, (byte) 135, (byte) 227, 9, (byte) 31,
        (byte) 226, (byte) 128, (byte) 84, (byte) 92, (byte) 116, (byte) 241, (byte) 70, (byte) 248, (byte) 27,
        (byte) 227, (byte) 193, (byte) 62, (byte) 5, (byte) 91, (byte) 241, (byte) 145, (byte) 224, (byte) 205,
        (byte) 141, (byte) 176, (byte) 184, (byte) 133, (byte) 239, (byte) 43, (byte) 81, (byte) 103, (byte) 9,
        (byte) 161, (byte) 153, (byte) 157, (byte) 179, (byte) 104, (byte) 123, (byte) 51, (byte) 189, (byte) 34,
        (byte) 152, (byte) 69, (byte) 97, (byte) 69, (byte) 78, (byte) 93, (byte) 140, (byte) 131, (byte) 87,
        (byte) 182, (byte) 169, (byte) 101, (byte) 92, (byte) 142, (byte) 3, (byte) 22, (byte) 167, (byte) 8,
        (byte) 212, (byte) 56, (byte) 35, (byte) 79, (byte) 210, (byte) 222, (byte) 192, (byte) 208, (byte) 252,
        (byte) 49, (byte) 109, (byte) 138, (byte) 173, (byte) 253, (byte) 210, (byte) 166, (byte) 201, (byte) 63,
        (byte) 102, (byte) 74, (byte) 5, (byte) 158, (byte) 41, (byte) 90, (byte) 144, (byte) 108, (byte) 160,
        (byte) 79, (byte) 10, (byte) 89, (byte) 222, (byte) 231, (byte) 172, (byte) 31, (byte) 227, (byte) 197,
        (byte) 0, (byte) 19, (byte) 72, (byte) 81, (byte) 138, (byte) 78, (byte) 136, (byte) 221, (byte) 121,
        (byte) 118, (byte) 196, (byte) 17, (byte) 146, (byte) 10, (byte) 244, (byte) 188, (byte) 72, (byte) 113,
        (byte) 55, (byte) 221, (byte) 162, (byte) 217, (byte) 171, (byte) 27, (byte) 57, (byte) 233, (byte) 210,
        (byte) 101, (byte) 236, (byte) 154, (byte) 199, (byte) 56, (byte) 138, (byte) 239, (byte) 101, (byte) 48,
        (byte) 198, (byte) 186, (byte) 202, (byte) 160, (byte) 76, (byte) 111, (byte) 234, (byte) 71, (byte) 57,
        (byte) 183, (byte) 5, (byte) 211, (byte) 171, (byte) 136, (byte) 126, (byte) 64, 40, (byte) 75, (byte) 58,
        (byte) 89, (byte) 244, (byte) 254, (byte) 107, (byte) 84, (byte) 103, (byte) 7, (byte) 236, (byte) 69,
        (byte) 163, (byte) 18, (byte) 180, (byte) 251, 58, (byte) 153, (byte) 46, (byte) 151, (byte) 174, (byte) 12,
        (byte) 103, (byte) 197, (byte) 181, (byte) 161, (byte) 162, (byte) 55, (byte) 250, (byte) 235, (byte) 123,
        (byte) 110, 17, (byte) 11, (byte) 158, (byte) 24, (byte) 47, (byte) 133, (byte) 8, (byte) 199, (byte) 235,
        (byte) 107, (byte) 126, (byte) 130, (byte) 246, (byte) 73, (byte) 195, (byte) 20, 108, (byte) 202, (byte) 176,
        (byte) 214, (byte) 187, (byte) 45, (byte) 146, (byte) 182, (byte) 118, (byte) 54, (byte) 32, (byte) 200,
        (byte) 61, (byte) 201, (byte) 71, (byte) 243, 1, (byte) 255, (byte) 131, (byte) 84, (byte) 37, (byte) 111,
        (byte) 211, (byte) 168, (byte) 228, (byte) 45, (byte) 192, (byte) 118, (byte) 27, (byte) 197, (byte) 235,
        (byte) 232, 36, (byte) 10, (byte) 230, (byte) 248, (byte) 190, (byte) 82, (byte) 182, (byte) 140, (byte) 35,
        (byte) 204, (byte) 108, (byte) 190, (byte) 253, (byte) 186, (byte) 186, (byte) 27 };
    // D (private key exponent)
    byte[] d = { (byte) 144, (byte) 183, (byte) 109, (byte) 34, (byte) 62, (byte) 134, (byte) 108, (byte) 57,
        (byte) 44, (byte) 252, (byte) 10, (byte) 66, (byte) 73, (byte) 54, (byte) 16, (byte) 181, (byte) 233,
        (byte) 92, (byte) 54, (byte) 219, (byte) 101, (byte) 42, (byte) 35, (byte) 178, (byte) 63, (byte) 51,
        (byte) 43, (byte) 92, (byte) 119, (byte) 136, (byte) 251, (byte) 41, 53, (byte) 23, (byte) 191, (byte) 164,
        (byte) 164, (byte) 60, (byte) 88, (byte) 227, (byte) 229, (byte) 152, (byte) 228, (byte) 213, (byte) 149,
        (byte) 228, (byte) 169, (byte) 237, 104, (byte) 71, (byte) 151, (byte) 75, (byte) 88, (byte) 252, (byte) 216,
        (byte) 77, (byte) 251, (byte) 231, (byte) 28, (byte) 97, (byte) 88, (byte) 193, (byte) 215, (byte) 202,
        (byte) 248, (byte) 216, (byte) 121, (byte) 195, (byte) 211, (byte) 245, (byte) 250, (byte) 112, (byte) 71,
        (byte) 243, (byte) 61, (byte) 129, (byte) 95, (byte) 39, (byte) 244, (byte) 122, (byte) 225, (byte) 217,
        (byte) 169, (byte) 211, (byte) 165, (byte) 48, (byte) 253, (byte) 220, (byte) 59, (byte) 122, (byte) 219,
        (byte) 42, (byte) 86, (byte) 223, (byte) 32, (byte) 236, 39, (byte) 48, (byte) 103, (byte) 78, (byte) 122,
        (byte) 216, (byte) 187, (byte) 88, (byte) 176, (byte) 89, (byte) 24, (byte) 1, (byte) 42, (byte) 177,
        (byte) 24, (byte) 99, (byte) 142, (byte) 170, (byte) 1, (byte) 146, (byte) 43, (byte) 3, (byte) 108, (byte) 64,
        (byte) 194, (byte) 121, (byte) 182, (byte) 95, (byte) 187, (byte) 134, (byte) 71, (byte) 88, 96, (byte) 134,
        (byte) 74, (byte) 131, (byte) 167, (byte) 69, (byte) 106, (byte) 143, (byte) 121, (byte) 27, (byte) 72,
        (byte) 44, (byte) 245, (byte) 95, (byte) 39, (byte) 194, (byte) 179, (byte) 175, (byte) 203, (byte) 122,
        (byte) 16, (byte) 112, (byte) 183, (byte) 17, (byte) 200, (byte) 202, (byte) 31, (byte) 17, (byte) 138,
        (byte) 156, (byte) 184, (byte) 210, (byte) 157, (byte) 184, (byte) 154, (byte) 131, (byte) 128, (byte) 110,
        (byte) 12, (byte) 85, (byte) 195, (byte) 122, (byte) 241, (byte) 79, (byte) 251, (byte) 229, (byte) 183,
        (byte) 117, 21, (byte) 123, (byte) 133, (byte) 142, (byte) 220, (byte) 153, (byte) 9, (byte) 59, (byte) 57,
        (byte) 105, (byte) 81, (byte) 255, (byte) 138, (byte) 77, (byte) 82, (byte) 54, 62, (byte) 216, (byte) 38,
        (byte) 249, (byte) 208, (byte) 17, (byte) 197, (byte) 49, (byte) 45, (byte) 19, (byte) 232, (byte) 157,
        (byte) 251, (byte) 131, (byte) 137, (byte) 175, 72, (byte) 126, (byte) 43, (byte) 229, (byte) 69, (byte) 179,
        (byte) 117, (byte) 82, (byte) 157, (byte) 213, (byte) 83, (byte) 35, (byte) 57, (byte) 210, (byte) 197,
        (byte) 252, (byte) 171, (byte) 143, (byte) 194, (byte) 11, (byte) 47, (byte) 163, (byte) 6, (byte) 253,
        (byte) 75, (byte) 252, (byte) 96, (byte) 11, (byte) 187, (byte) 84, (byte) 130, (byte) 210, 7, (byte) 121,
        (byte) 78, (byte) 91, (byte) 79, (byte) 57, (byte) 251, (byte) 138, (byte) 132, (byte) 220, (byte) 60,
        (byte) 224, (byte) 173, (byte) 56, (byte) 224, (byte) 201 };

    BigInteger N = new BigInteger(1, n);
    BigInteger E = new BigInteger(1, e);
    BigInteger D = new BigInteger(1, d);

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec(N, E);
    RSAPrivateKeySpec privKeySpec = new RSAPrivateKeySpec(N, D);
    RSAPublicKey aRsaPublicKey = (RSAPublicKey) keyFactory.generatePublic(pubKeySpec);
    RSAPrivateKey aRsaPrivKey = (RSAPrivateKey) keyFactory.generatePrivate(privKeySpec);

    SecretKey secretKey = new SecretKeySpec(CMK, "AES");

    String encrypted = JcBase.encrypt(Plaintext, Header, aRsaPublicKey, secretKey, EncodedIV);
    String[] split = encrypted.split("\\.");

    assertEquals(5, split.length);
    byte[] cleartextBytes = JcBase.decrypt(encrypted, aRsaPrivKey);
    assertTrue(Arrays.equals(Plaintext, cleartextBytes));

    String JWE = "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.M2XxpbORKezKSzzQL_95-GjiudRBTqn_omS8z9xgoRb7L0Jw5UsEbxmtyHn2T71mrZLkjg4Mp8gbhYoltPkEOHvAopz25-vZ8C2e1cOaAo5WPcbSIuFcB4DjBOM3t0UAO6JHkWLuAEYoe58lcxIQneyKdaYSLbV9cKqoUoFQpvKWYRHZbfszIyfsa18rmgTjzrtLDTPnc09DSJE24aQ8w3i8RXEDthW9T1J6LsTH_vwHdwUgkI-tC2PNeGrnM-dNSfzF3Y7-lwcGy0FsdXkPXytvDV7y4pZeeUiQ-0VdibIN2AjjfW60nfrPuOjepMFG6BBBbR37pHcyzext9epOAQ.48V1_ALb6US04U3b._e21tGGhac_peEFkLXr2dMPUZiUkrw.7V5ZDko0v_mf2PAc4JMiUg";

    cleartextBytes = JcBase.decrypt(JWE, aRsaPrivKey);
    assertTrue(Arrays.equals(Plaintext, cleartextBytes));
    System.out.println("plaintext: " + new String(Plaintext));
  }

  public void testAesKeyWrap() throws Exception {
    byte[] SharedKey = { 25, (byte) 172, (byte) 32, (byte) 130, (byte) 225, (byte) 114, (byte) 26, (byte) 181,
        (byte) 138, (byte) 106, (byte) 254, (byte) 192, (byte) 95, (byte) 133, (byte) 74, (byte) 82 };
    byte[] CMK = { 64, (byte) 154, (byte) 239, (byte) 170, (byte) 64, (byte) 40, (byte) 195, (byte) 99, (byte) 19,
        (byte) 84, (byte) 192, (byte) 142, (byte) 192, (byte) 238, (byte) 207, (byte) 217 };
    byte[] IV = { (byte) 253, (byte) 220, (byte) 80, (byte) 25, (byte) 166, (byte) 152, (byte) 178, (byte) 168,
        (byte) 97, (byte) 99, (byte) 67, (byte) 89 };
    byte[] Plaintext = { 84, (byte) 104, (byte) 101, (byte) 32, (byte) 116, (byte) 114, (byte) 117, (byte) 101,
        (byte) 32, (byte) 115, (byte) 105, (byte) 103, (byte) 110, (byte) 32, (byte) 111, (byte) 102, 32, (byte) 105,
        (byte) 110, (byte) 116, (byte) 101, (byte) 108, (byte) 108, (byte) 105, (byte) 103, (byte) 101, (byte) 110,
        (byte) 99, (byte) 101, (byte) 32, (byte) 105, (byte) 115, 32, (byte) 110, (byte) 111, (byte) 116, (byte) 32,
        (byte) 107, (byte) 110, (byte) 111, (byte) 119, (byte) 108, (byte) 101, (byte) 100, (byte) 103, (byte) 101,
        (byte) 32, (byte) 98, 117, (byte) 116, (byte) 32, (byte) 105, (byte) 109, (byte) 97, (byte) 103, (byte) 105,
        (byte) 110, (byte) 97, (byte) 116, (byte) 105, (byte) 111, (byte) 110, (byte) 46 };
    String Header = "{\"alg\":\"A128KW\",\"enc\":\"A128GCM\"}";
    byte[] HeaderBytes = { 123, (byte) 34, (byte) 97, (byte) 108, (byte) 103, (byte) 34, (byte) 58, (byte) 34,
        (byte) 65, (byte) 49, (byte) 50, (byte) 56, (byte) 75, (byte) 87, (byte) 34, (byte) 44, 34, (byte) 101,
        (byte) 110, (byte) 99, (byte) 34, (byte) 58, (byte) 34, (byte) 65, (byte) 49, (byte) 50, (byte) 56, (byte) 71,
        (byte) 67, (byte) 77, (byte) 34, (byte) 125 };
    assertTrue(Arrays.equals(Header.getBytes(), HeaderBytes));
    String EncodedHeader = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0";
    assertEquals(EncodedHeader,
        Base64.encodeBytes(HeaderBytes, org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL));
    String EncodedIV = "_dxQGaaYsqhhY0NZ";
    assertEquals(EncodedIV,
        Base64.encodeBytes(IV, org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL));

    byte[] EncryptedKey = { (byte) 164, (byte) 255, (byte) 251, (byte) 1, (byte) 64, (byte) 200, (byte) 65, (byte) 200,
        (byte) 34, (byte) 197, (byte) 81, (byte) 143, (byte) 43, (byte) 211, (byte) 240, (byte) 38, (byte) 191,
        (byte) 161, (byte) 181, (byte) 117, (byte) 119, (byte) 68, (byte) 44, (byte) 80 };
    assertEquals(24, EncryptedKey.length);
    String EncodedEncryptedKey = "pP_7AUDIQcgixVGPK9PwJr-htXV3RCxQ";
    assertEquals(EncodedEncryptedKey,
        Base64.encodeBytes(EncryptedKey, org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL));

    String AdditionalAuthenticatedData = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.pP_7AUDIQcgixVGPK9PwJr-htXV3RCxQ._dxQGaaYsqhhY0NZ";
    byte[] AdditionalAuthenticatedDataBytes = { 101, (byte) 121, (byte) 74, (byte) 104, (byte) 98, (byte) 71,
        (byte) 99, (byte) 105, (byte) 79, (byte) 105, (byte) 74, (byte) 66, (byte) 77, (byte) 84, (byte) 73, (byte) 52,
        83, (byte) 49, (byte) 99, (byte) 105, (byte) 76, (byte) 67, (byte) 74, (byte) 108, (byte) 98, (byte) 109,
        (byte) 77, (byte) 105, (byte) 79, (byte) 105, (byte) 74, (byte) 66, 77, (byte) 84, (byte) 73, (byte) 52,
        (byte) 82, (byte) 48, (byte) 78, (byte) 78, (byte) 73, (byte) 110, (byte) 48, (byte) 46, (byte) 112, (byte) 80,
        (byte) 95, (byte) 55, 65, (byte) 85, (byte) 68, (byte) 73, (byte) 81, (byte) 99, (byte) 103, (byte) 105,
        (byte) 120, (byte) 86, (byte) 71, (byte) 80, (byte) 75, (byte) 57, (byte) 80, (byte) 119, 74, (byte) 114,
        (byte) 45, (byte) 104, (byte) 116, (byte) 88, (byte) 86, (byte) 51, (byte) 82, (byte) 67, (byte) 120,
        (byte) 81, (byte) 46, (byte) 95, (byte) 100, (byte) 120, 81, (byte) 71, (byte) 97, (byte) 97, (byte) 89,
        (byte) 115, (byte) 113, (byte) 104, (byte) 104, (byte) 89, (byte) 48, (byte) 78, (byte) 90 };
    assertEquals(AdditionalAuthenticatedData, new String(AdditionalAuthenticatedDataBytes));

    byte[] Ciphertext = { (byte) 227, (byte) 12, (byte) 89, (byte) 132, (byte) 185, (byte) 16, (byte) 248, (byte) 93,
        (byte) 145, (byte) 87, (byte) 53, (byte) 130, (byte) 95, (byte) 115, (byte) 62, (byte) 104, (byte) 138,
        (byte) 96, (byte) 109, (byte) 71, (byte) 124, (byte) 211, (byte) 165, (byte) 103, (byte) 202, (byte) 99,
        (byte) 21, (byte) 193, (byte) 4, (byte) 226, (byte) 84, (byte) 229, (byte) 254, (byte) 106, (byte) 144,
        (byte) 241, (byte) 39, (byte) 86, (byte) 148, (byte) 132, (byte) 160, (byte) 104, (byte) 88, (byte) 232,
        (byte) 228, (byte) 109, (byte) 85, (byte) 7, 86, (byte) 80, (byte) 134, (byte) 106, (byte) 166, (byte) 24,
        (byte) 92, (byte) 199, (byte) 210, (byte) 188, (byte) 153, (byte) 187, (byte) 218, (byte) 69, (byte) 227 };
    String EncodedCiphertext = "4wxZhLkQ-F2RVzWCX3M-aIpgbUd806VnymMVwQTiVOX-apDxJ1aUhKBoWOjkbVUHVlCGaqYYXMfSvJm72kXj";
    assertEquals(EncodedCiphertext,
        Base64.encodeBytes(Ciphertext, org.jsoncrypto.util.Base64.DONT_BREAK_LINES | org.jsoncrypto.util.Base64.URL));

    byte[] AuthenticationTag = { (byte) 154, (byte) 35, (byte) 80, (byte) 107, (byte) 37, (byte) 148, (byte) 81,
        (byte) 6, (byte) 103, (byte) 4, (byte) 60, (byte) 206, (byte) 171, (byte) 165, (byte) 113, (byte) 67 };
    String EncodedAuthenticationTag = "miNQayWUUQZnBDzOq6VxQw";
    assertEquals(
        EncodedAuthenticationTag,
        Base64.encodeBytes(AuthenticationTag, org.jsoncrypto.util.Base64.DONT_BREAK_LINES
            | org.jsoncrypto.util.Base64.URL));

    SecretKey key = new SecretKeySpec(SharedKey, "AES");

    String encrypted = JcBase.encrypt(Plaintext, Header, key, new SecureRandom());

    String JWE = "eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIn0.pP_7AUDIQcgixVGPK9PwJr-htXV3RCxQ._dxQGaaYsqhhY0NZ.4wxZhLkQ-F2RVzWCX3M-aIpgbUd806VnymMVwQTiVOX-apDxJ1aUhKBoWOjkbVUHVlCGaqYYXMfSvJm72kXj.miNQayWUUQZnBDzOq6VxQw";

    byte[] decrypted = JcBase.decrypt(JWE, key);
    assertTrue(Arrays.equals(Plaintext, decrypted));
    System.out.println(new String(decrypted));
  }

  public void testIdToken() throws Exception {
    String id_token = "eyJhbGciOiJSUzI1NiIsICJqd2siOnsiYWxnIjoiUlNBIiwiZXhwIjoiQVFBQiIsImtpZCI6IjJxMGxLcWw3Zk03N3pnd3lzd0hTWGRyUklKbyIsIm1vZCI6IkFOYlBWUzJ5UVctQkYtd0l4RWVHczRHaVVYMnNscGw4dnVKeV9GbUUyT0drUkZQXzBhWm51VV9RQWpuVWNzNWtOSXR3djJiY2h2ZzUwakFPWV9ZV3lQdVB6YUJROU9zcE81UHUtY3ZkaExlc0hPclJINjQyTnZxRVQwMnZlZExUcWxSaXFhYXc3TkZjTFlKeENzU0otZUtUZUxYbUNqQ3FOd0xOWXZyd2pwc3lEQXhKQjdodnlUTzBwNVFZRGJsbnNHWVBvaDk2eXpxbTcwU1daR2JJWVN0MFNhSDY4S05zcXNfMXpuVG96Mnp6eVV0TGJlQmVMNV9idXQ5Vy1NSjZxUWdhbkZMVVVwWjFpRlMycFByQ1pjZktFY0x0cXYxLUIyd3F1Uks5aWs5LVZnSkoydWVtcHhCN3c3MXZjZjhNd09iTC1waFljeDEwRVBRdkt1ZzFZVWsifX0.eyJzdGF0ZSI6ImFiY2RlZmciLCJ1c2VyaW5mbyI6eyJwaWN0dXJlIjoiIiwiZmFtaWx5X25hbWUiOiJOZW5ua2VyIiwidXNlcl9pZCI6IjJxMGxLcWw3Zk03N3pnd3lzd0hTWGRyUklKbyIsImdpdmVuX25hbWUiOiJBeGVsIiwiZW1haWwiOiJheGVsQG5lbm5rZXIuZGUiLCJuYW1lIjoiQXhlbCBOZW5ua2VyIn0sInNjb3BlIjoib3BlbmlkIiwiY2xpZW50X2lkIjoiMnEwbEtxbDdmTTc3emd3eXN3SFNYZHJSSUpvIiwicmVzcG9uc2VfdHlwZSI6ImlkX3Rva2VuIiwiaWRfdG9rZW4iOnsiYXV0aF90aW1lIjoxMzQ5NzcxNTAyNzg3LCJ1c2VyX2lkIjoiMnEwbEtxbDdmTTc3emd3eXN3SFNYZHJSSUpvIn19.GepztuTP7hnmm2pr0wg_o6K7Lc-zZLqTU1ymNho7An0sVzjthZHqzXu_2pTYVlwRHEZGcvxS8RYW5DwfbG1PTo3dJLhQJe21vLkAPEGVqSa1ZIvQg5wx6-QS5GP6u5-uOb7FH09XIXFTte8GZMNTRbhLeke5JeYKf9RSlKCa_lyTiTQmuHuVJikxxnG9yAGJzV1C1Ra8byYAXGlcO-j_Ma572CqgRP-Pv4_CbR5oKADvCNgeTvqWiLnLID-zlDG4ZFdTW91XnotaFuH7RD16h5F_0suf8YK8ozfM4CJNALPUgfe4q1nXSpJ-SUSLqP5M6lYcogH1Wq6WUIUmT-Odbg";
    String[] split = id_token.split("\\.");
    assertEquals(3, split.length);
    String jwtHeaderStrB64 = split[0];
    assertEquals(
        "eyJhbGciOiJSUzI1NiIsICJqd2siOnsiYWxnIjoiUlNBIiwiZXhwIjoiQVFBQiIsImtpZCI6IjJxMGxLcWw3Zk03N3pnd3lzd0hTWGRyUklKbyIsIm1vZCI6IkFOYlBWUzJ5UVctQkYtd0l4RWVHczRHaVVYMnNscGw4dnVKeV9GbUUyT0drUkZQXzBhWm51VV9RQWpuVWNzNWtOSXR3djJiY2h2ZzUwakFPWV9ZV3lQdVB6YUJROU9zcE81UHUtY3ZkaExlc0hPclJINjQyTnZxRVQwMnZlZExUcWxSaXFhYXc3TkZjTFlKeENzU0otZUtUZUxYbUNqQ3FOd0xOWXZyd2pwc3lEQXhKQjdodnlUTzBwNVFZRGJsbnNHWVBvaDk2eXpxbTcwU1daR2JJWVN0MFNhSDY4S05zcXNfMXpuVG96Mnp6eVV0TGJlQmVMNV9idXQ5Vy1NSjZxUWdhbkZMVVVwWjFpRlMycFByQ1pjZktFY0x0cXYxLUIyd3F1Uks5aWs5LVZnSkoydWVtcHhCN3c3MXZjZjhNd09iTC1waFljeDEwRVBRdkt1ZzFZVWsifX0",
        jwtHeaderStrB64);
    String jwtHeaderStr = new String(Base64.decodeUrl(jwtHeaderStrB64));
    String expectedHeaderStr = "{\"alg\":\"RS256\", \"jwk\":{\"alg\":\"RSA\",\"exp\":\"AQAB\",\"kid\":\"2q0lKql7fM77zgwyswHSXdrRIJo\",\"mod\":\"ANbPVS2yQW-BF-wIxEeGs4GiUX2slpl8vuJy_FmE2OGkRFP_0aZnuU_QAjnUcs5kNItwv2bchvg50jAOY_YWyPuPzaBQ9OspO5Pu-cvdhLesHOrRH642NvqET02vedLTqlRiqaaw7NFcLYJxCsSJ-eKTeLXmCjCqNwLNYvrwjpsyDAxJB7hvyTO0p5QYDblnsGYPoh96yzqm70SWZGbIYSt0SaH68KNsqs_1znToz2zzyUtLbeBeL5_but9W-MJ6qQganFLUUpZ1iFS2pPrCZcfKEcLtqv1-B2wquRK9ik9-VgJJ2uempxB7w71vcf8MwObL-phYcx10EPQvKug1YUk\"}}";
    assertEquals(expectedHeaderStr, jwtHeaderStr);
    JSONObject jwtHeader = new JSONObject(expectedHeaderStr);
    assertEquals("RS256", jwtHeader.getString("alg"));
    JSONObject jwk = jwtHeader.getJSONObject("jwk");
    assertEquals("RSA", jwk.getString("alg"));
    String modulusB64 = jwk.getString("mod");
    assertEquals(
        "ANbPVS2yQW-BF-wIxEeGs4GiUX2slpl8vuJy_FmE2OGkRFP_0aZnuU_QAjnUcs5kNItwv2bchvg50jAOY_YWyPuPzaBQ9OspO5Pu-cvdhLesHOrRH642NvqET02vedLTqlRiqaaw7NFcLYJxCsSJ-eKTeLXmCjCqNwLNYvrwjpsyDAxJB7hvyTO0p5QYDblnsGYPoh96yzqm70SWZGbIYSt0SaH68KNsqs_1znToz2zzyUtLbeBeL5_but9W-MJ6qQganFLUUpZ1iFS2pPrCZcfKEcLtqv1-B2wquRK9ik9-VgJJ2uempxB7w71vcf8MwObL-phYcx10EPQvKug1YUk",
        modulusB64);
    String expB64 = jwk.getString("exp");
    assertEquals("AQAB", expB64);
    byte[] modulusBytes = Base64.decodeUrl(modulusB64);
    BigInteger mod = new BigInteger(1, modulusBytes);
    byte[] expBytes = Base64.decodeUrl(expB64);
    BigInteger exp = new BigInteger(1, expBytes);
    RSAPublicKeySpec keySpec = new RSAPublicKeySpec(mod, exp);
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
    assertTrue(JcBase.verify(id_token, publicKey));
  }

  // public void testSignedGabunomiIdToken() throws Exception {
  // String id_token =
  // "eyJhbGciOiJSUzI1NiIsICJqd2siOnsibW9kIjoiNjV5eHFKaXhHNVpxUVVjblFhc05pRk13TndBTWFRcnFmMXktUGEtTnQ0QUNFUndiNURad05RSzVXUGhuZ3FjdzFLVng1T0lpRV9BdWNNNnBVSmVUVmp2TWM4X2V3ZG9MQWd2SGx3aDNFczRwWjhfand2bGpqYmdtdDRxdXU4RllzdmR3ZHVaMWJXV09QUWYzb0ZzZWplRGlrWk9TMGpBMzhBLTZnLVpQUmdMaWs0cHlybjJxRXJMR1RLelJNRjVfN3FIN2EyUmZoelN0TnIyNUVGZ2NpWUFmRTQtRDR3TEhHWm4wM0FPTVVLVTFjOEZiWDZtRkl2U2NiTEhza2I0bVpIT3NDWmN5M1YyaENDOU5pdUx0OG1tdTNVcnpvbGZ1YngwYUZnbkZFM1llbDkzZTRSakhOUlF6Qkp4TUZrcl9paUhDQUVXd1M1OTlWZ1FvUTcxYklRIiwiZXhwIjoiQVFBQiIsImFsZyI6InJzYSJ9fQ.eyJleHAiOjEzNTE4NjI5NTQxNzMsImF1ZCI6Imh0dHBzOi8vZ2FidW5vbWkubmV0L3NlbWluYXIvY2FsbGJhY2suaHRtbCIsImlzcyI6Imh0dHBzOi8vc2VsZi1pc3N1ZWQubWUiLCJ1c2VyX2p3ayI6eyJhbGciOiJSU0EifSwibmFtZSI6IkF4ZWwgTmVubmtlciIsImVtYWlsIjoiYXhlbC5uZW5ua2VyQHRlbGVrb20uZGUiLCJwaWN0dXJlIjoiaHR0cHM6Ly9saDMuZ29vZ2xldXNlcmNvbnRlbnQuY29tLy1ZLWxnUUhfbkVPVS9BQUFBQUFBQUFBSS9BQUFBQUFBQUNGTS8xbVNwT3k5MFJKQS9zMjUwLWMtay9waG90by5qcGcifQ.b0NZUUEtdmZIU0x1UUNXWl8tRjF4Tkh5UmFzc29HYjFjbWNFZDhMc0FoRDNpTHRvUDNBVlA2Tll5ekE5SnhjUjN5bXVJRFd3LXVPRUZ3SVlVWVR0ejM0MUFvRUxfOFJyVmFPX1FlVmNrcE1SY2E5TWtkTGMwYVVNSE1Xc2hNSVRsNWlScC13MnRJVndlTDkwZnBTejlMY2dBVktnQlBPN1pxWWFPNkdVcnRSVmpXUmNiMmN4X0ZTYVI0Q3EtWW5CNEI2QVZ5eFEyT1BSaE0tVVBXcUJHWWdybXJiN1NpdnJ5dDFWMUdCMnhJMUxPcHRacmVaRTlUZ3ZlRWcxR1lWNm0yZVNrYlFNQ1dWZHMwb2VQNk15cnpYLTByNVlSMFZEWGR1ZjVvRDYwalR4T045elpvdlp1UnFjRWdHaW1JWDh0T3JVdThmMUJkZ2d5dG5xeU5lUF9B";
  // String[] split = id_token.split("\\.");
  // assertEquals(3, split.length);
  // String jwtHeaderStrB64 = split[0];
  // assertEquals(
  // "eyJhbGciOiJSUzI1NiIsICJqd2siOnsibW9kIjoiNjV5eHFKaXhHNVpxUVVjblFhc05pRk13TndBTWFRcnFmMXktUGEtTnQ0QUNFUndiNURad05RSzVXUGhuZ3FjdzFLVng1T0lpRV9BdWNNNnBVSmVUVmp2TWM4X2V3ZG9MQWd2SGx3aDNFczRwWjhfand2bGpqYmdtdDRxdXU4RllzdmR3ZHVaMWJXV09QUWYzb0ZzZWplRGlrWk9TMGpBMzhBLTZnLVpQUmdMaWs0cHlybjJxRXJMR1RLelJNRjVfN3FIN2EyUmZoelN0TnIyNUVGZ2NpWUFmRTQtRDR3TEhHWm4wM0FPTVVLVTFjOEZiWDZtRkl2U2NiTEhza2I0bVpIT3NDWmN5M1YyaENDOU5pdUx0OG1tdTNVcnpvbGZ1YngwYUZnbkZFM1llbDkzZTRSakhOUlF6Qkp4TUZrcl9paUhDQUVXd1M1OTlWZ1FvUTcxYklRIiwiZXhwIjoiQVFBQiIsImFsZyI6InJzYSJ9fQ",
  // jwtHeaderStrB64);
  // String jwtHeaderStr = new String(Base64.decodeUrl(jwtHeaderStrB64));
  // String expectedHeaderStr =
  // "{\"alg\":\"RS256\", \"jwk\":{\"mod\":\"65yxqJixG5ZqQUcnQasNiFMwNwAMaQrqf1y-Pa-Nt4ACERwb5DZwNQK5WPhngqcw1KVx5OIiE_AucM6pUJeTVjvMc8_ewdoLAgvHlwh3Es4pZ8_jwvljjbgmt4quu8FYsvdwduZ1bWWOPQf3oFsejeDikZOS0jA38A-6g-ZPRgLik4pyrn2qErLGTKzRMF5_7qH7a2RfhzStNr25EFgciYAfE4-D4wLHGZn03AOMUKU1c8FbX6mFIvScbLHskb4mZHOsCZcy3V2hCC9NiuLt8mmu3Urzolfubx0aFgnFE3Yel93e4RjHNRQzBJxMFkr_iiHCAEWwS599VgQoQ71bIQ\",\"exp\":\"AQAB\",\"alg\":\"rsa\"}}";
  // assertEquals(expectedHeaderStr, jwtHeaderStr);
  // JSONObject jwtHeader = new JSONObject(expectedHeaderStr);
  // assertEquals("RS256", jwtHeader.getString("alg"));
  // JSONObject jwk = jwtHeader.getJSONObject("jwk");
  // assertEquals("rsa", jwk.getString("alg"));
  // String modulusB64 = jwk.getString("mod");
  // assertEquals(
  // "65yxqJixG5ZqQUcnQasNiFMwNwAMaQrqf1y-Pa-Nt4ACERwb5DZwNQK5WPhngqcw1KVx5OIiE_AucM6pUJeTVjvMc8_ewdoLAgvHlwh3Es4pZ8_jwvljjbgmt4quu8FYsvdwduZ1bWWOPQf3oFsejeDikZOS0jA38A-6g-ZPRgLik4pyrn2qErLGTKzRMF5_7qH7a2RfhzStNr25EFgciYAfE4-D4wLHGZn03AOMUKU1c8FbX6mFIvScbLHskb4mZHOsCZcy3V2hCC9NiuLt8mmu3Urzolfubx0aFgnFE3Yel93e4RjHNRQzBJxMFkr_iiHCAEWwS599VgQoQ71bIQ",
  // modulusB64);
  // String expB64 = jwk.getString("exp");
  // assertEquals("AQAB", expB64);
  // byte[] modulusBytes = Base64.decodeUrl(modulusB64);
  // BigInteger mod = new BigInteger(1, modulusBytes);
  // byte[] expBytes = Base64.decodeUrl(expB64);
  // BigInteger exp = new BigInteger(1, expBytes);
  // RSAPublicKeySpec keySpec = new RSAPublicKeySpec(mod, exp);
  // KeyFactory keyFactory = KeyFactory.getInstance("RSA");
  // RSAPublicKey publicKey = (RSAPublicKey) keyFactory.generatePublic(keySpec);
  // assertTrue(JcBase.verify(id_token, publicKey));
  // }

  static String PublicKey2String(RSAPublicKeySpec publicKeySpec) throws Exception {
    org.bouncycastle.asn1.pkcs.RSAPublicKey pStruct = new org.bouncycastle.asn1.pkcs.RSAPublicKey(
        publicKeySpec.getModulus(), publicKeySpec.getPublicExponent());
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ASN1OutputStream aos = new ASN1OutputStream(baos);
    aos.writeObject(pStruct.toASN1Primitive());
    aos.close();
    return Base64.encodeBytesNoBreaks(baos.toByteArray());
  }

  static RSAPublicKeySpec getFromString(String keystr) throws Exception {
    byte[] keyBytes = Base64.decode(keystr);
    if (keyBytes == null) {
      keyBytes = Base64.decodeUrl(keystr);
      if (keyBytes == null) {
        throw new NullPointerException("Cannot base64 decode " + keystr);
      }
    }
    ASN1InputStream in = new ASN1InputStream(keyBytes);
    try {
      ASN1Primitive obj = in.readObject();

      ASN1Sequence seq = (ASN1Sequence) obj;
      SubjectPublicKeyInfo pkInfo = SubjectPublicKeyInfo.getInstance(seq);
      org.bouncycastle.asn1.pkcs.RSAPublicKey pStruct = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(pkInfo
          .parsePublicKey());

      RSAPublicKeySpec spec = new RSAPublicKeySpec(pStruct.getModulus(), pStruct.getPublicExponent());
      return spec;
    } catch (Exception e) {
      throw e;
    } finally {
      in.close();
    }
  }

  public void testPublicKeyInfoConversion() throws Exception {
    // PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
    // privateKey.getEncoded());

    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(rsaPublicKey.getEncoded());
    PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
    assertEquals("RSA", publicKey.getAlgorithm());

    String gabunomiPublicKeyB64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4OTqe0p1tgEoOVtDzjQI"
        + "yP1Ipo8ivqTIeH4yH9kLzI4fCKx6ggZJ3h9ecj4p5E355umCThN/1doBc/tq18VG"
        + "lNtyDNxCh45Z1zGYJKwZxaVaWQXlB2gfgnko1D+Zw9KIlipQHtnhJw/qREEIp4YO"
        + "gaGcSZBCcQQ4DYCOjfTTbKUXSTlrlOgflfgTiyhUFuiKWkoeivwASigL76PtYNYc"
        + "n+dlYKYB/vSQ2CY7FtaDcr22EdqUDVPLNg1+K1rsvHvllP7iTnXA5IgxT5JELdrk"
        + "KX9Ek68zDzelOaJxs2tbkkwbqSLQfREzQ/yGAIOW9rZVqlaVBEBzUYzREmeybVq3" + "gwIDAQAB";
    x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decode(gabunomiPublicKeyB64));
    publicKey = keyFactory.generatePublic(x509EncodedKeySpec);

    String gabPublicKeyB64 = Base64.encodeBytes(publicKey.getEncoded(), Base64.DONT_BREAK_LINES | Base64.URL);
    String expected = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4OTqe0p1tgEoOVtDzjQIyP1Ipo8ivqTIeH4yH9kLzI4fCKx6ggZJ3h9ecj4p5E355umCThN_1doBc_tq18VGlNtyDNxCh45Z1zGYJKwZxaVaWQXlB2gfgnko1D-Zw9KIlipQHtnhJw_qREEIp4YOgaGcSZBCcQQ4DYCOjfTTbKUXSTlrlOgflfgTiyhUFuiKWkoeivwASigL76PtYNYcn-dlYKYB_vSQ2CY7FtaDcr22EdqUDVPLNg1-K1rsvHvllP7iTnXA5IgxT5JELdrkKX9Ek68zDzelOaJxs2tbkkwbqSLQfREzQ_yGAIOW9rZVqlaVBEBzUYzREmeybVq3gwIDAQAB";
    assertEquals(expected, gabPublicKeyB64);

    RSAPublicKeySpec spec = getFromString(gabunomiPublicKeyB64);
    assertEquals(65537, spec.getPublicExponent().longValue());

    ASN1InputStream ais = null;
    try {
      byte[] keyBytes = Base64.decode(gabunomiPublicKeyB64);
      ais = new ASN1InputStream(keyBytes);
      Object asnObject = ais.readObject();
      ASN1Sequence sequence = (ASN1Sequence) asnObject;
      assertEquals(2, sequence.size());
      ASN1Encodable oid = sequence.getObjectAt(0);
      DLSequence oidSeq = (DLSequence) oid;
      assertEquals(2, oidSeq.size());
      ASN1Encodable pub = sequence.getObjectAt(1);
      DERBitString bitStr = (DERBitString) pub;
      byte[] bytes = bitStr.getBytes();
      byte[] expectedBytes = { 48, -126, 1, 10, 2, -126, 1, 1, 0, -32, -28, -22, 123, 74, 117, -74, 1, 40, 57, 91, 67,
          -50, 52, 8, -56, -3, 72, -90, -113, 34, -66, -92, -56, 120, 126, 50, 31, -39, 11, -52, -114, 31, 8, -84, 122,
          -126, 6, 73, -34, 31, 94, 114, 62, 41, -28, 77, -7, -26, -23, -126, 78, 19, 127, -43, -38, 1, 115, -5, 106,
          -41, -59, 70, -108, -37, 114, 12, -36, 66, -121, -114, 89, -41, 49, -104, 36, -84, 25, -59, -91, 90, 89, 5,
          -27, 7, 104, 31, -126, 121, 40, -44, 63, -103, -61, -46, -120, -106, 42, 80, 30, -39, -31, 39, 15, -22, 68,
          65, 8, -89, -122, 14, -127, -95, -100, 73, -112, 66, 113, 4, 56, 13, -128, -114, -115, -12, -45, 108, -91,
          23, 73, 57, 107, -108, -24, 31, -107, -8, 19, -117, 40, 84, 22, -24, -118, 90, 74, 30, -118, -4, 0, 74, 40,
          11, -17, -93, -19, 96, -42, 28, -97, -25, 101, 96, -90, 1, -2, -12, -112, -40, 38, 59, 22, -42, -125, 114,
          -67, -74, 17, -38, -108, 13, 83, -53, 54, 13, 126, 43, 90, -20, -68, 123, -27, -108, -2, -30, 78, 117, -64,
          -28, -120, 49, 79, -110, 68, 45, -38, -28, 41, 127, 68, -109, -81, 51, 15, 55, -91, 57, -94, 113, -77, 107,
          91, -110, 76, 27, -87, 34, -48, 125, 17, 51, 67, -4, -122, 0, -125, -106, -10, -74, 85, -86, 86, -107, 4, 64,
          115, 81, -116, -47, 18, 103, -78, 109, 90, -73, -125, 2, 3, 1, 0, 1 };
      assertTrue(Arrays.equals(expectedBytes, bytes));
      ASN1InputStream ais2 = new ASN1InputStream(bytes);
      int tag = ais2.read();
      assertEquals(48, tag);
      int length = ais2.read();
      assertEquals(130, length);
      int size = length & 0x7f;
      length = 0;
      for (int i = 0; i < size; i++) {
        int next = ais2.read();

        if (next < 0) {
          throw new EOFException("EOF found reading length");
        }

        length = (length << 8) + next;
      }
      assertEquals(266, length);
      ASN1Primitive p = ais2.readObject();
      ASN1Primitive e = ais2.readObject();
      // asnObject = ais2.readObject();
      ais2.close();
    } finally {
      ais.close();
    }
    // PKCS8EncodedKeySpec privateKeySpec = new
    // PKCS8EncodedKeySpec(rsaPrivKey.getEncoded());
    // PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);

  }

  // public void testPublicKeyInfoConversion2() throws Exception {
  // // PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(
  // // privateKey.getEncoded());
  //
  //
  // KeyFactory keyFactory = KeyFactory.getInstance("RSA");
  // X509EncodedKeySpec x509EncodedKeySpec = new
  // X509EncodedKeySpec(rsaPublicKey.getEncoded());
  // PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
  // assertEquals("RSA", publicKey.getAlgorithm());
  //
  // String pubKey =
  // "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA65yxqJixG5ZqQUcnQasNiFMwNwAMaQrqf1y-Pa-Nt4ACERwb5DZwNQK5WPhngqcw1KVx5OIiE_AucM6pUJeTVjvMc8_ewdoLAgvHlwh3Es4pZ8_jwvljjbgmt4quu8FYsvdwduZ1bWWOPQf3oFsejeDikZOS0jA38A-6g-ZPRgLik4pyrn2qErLGTKzRMF5_7qH7a2RfhzStNr25EFgciYAfE4-D4wLHGZn03AOMUKU1c8FbX6mFIvScbLHskb4mZHOsCZcy3V2hCC9NiuLt8mmu3Urzolfubx0aFgnFE3Yel93e4RjHNRQzBJxMFkr_iiHCAEWwS599VgQoQ71bIQIDAQAB";
  //
  // String gabPublicKeyB64 = Base64.encodeBytes(publicKey.getEncoded(),
  // Base64.DONT_BREAK_LINES|Base64.URL);
  // String expected =
  // "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQIDAQAB";
  // assertEquals(expected, gabPublicKeyB64);
  //
  // RSAPublicKeySpec spec = getFromString(pubKey);
  // assertEquals(65537, spec.getPublicExponent().longValue());
  //
  // ASN1InputStream ais = null;
  // try {
  // byte[] keyBytes = Base64.decodeUrl(pubKey);
  // ais = new ASN1InputStream(keyBytes);
  // Object asnObject = ais.readObject();
  // ASN1Sequence sequence = (ASN1Sequence) asnObject;
  // assertEquals(2, sequence.size());
  // ASN1Encodable oid = sequence.getObjectAt(0);
  // DLSequence oidSeq = (DLSequence)oid;
  // assertEquals(2, oidSeq.size());
  // ASN1Encodable pub = sequence.getObjectAt(1);
  // DERBitString bitStr = (DERBitString)pub;
  // byte[] bytes = bitStr.getBytes();
  // byte[] expectedBytes = {48, -126, 1, 10, 2, -126, 1, 1, 0, -21, -100, -79,
  // -88, -104, -79, 27, -106, 106, 65, 71, 39, 65, -85, 13, -120, 83, 48, 55,
  // 0, 12, 105, 10, -22, 127, 92, -66, 61, -81, -115, -73, -128, 2, 17, 28, 27,
  // -28, 54, 112, 53, 2, -71, 88, -8, 103, -126, -89, 48, -44, -91, 113, -28,
  // -30, 34, 19, -16, 46, 112, -50, -87, 80, -105, -109, 86, 59, -52, 115, -49,
  // -34, -63, -38, 11, 2, 11, -57, -105, 8, 119, 18, -50, 41, 103, -49, -29,
  // -62, -7, 99, -115, -72, 38, -73, -118, -82, -69, -63, 88, -78, -9, 112,
  // 118, -26, 117, 109, 101, -114, 61, 7, -9, -96, 91, 30, -115, -32, -30,
  // -111, -109, -110, -46, 48, 55, -16, 15, -70, -125, -26, 79, 70, 2, -30,
  // -109, -118, 114, -82, 125, -86, 18, -78, -58, 76, -84, -47, 48, 94, 127,
  // -18, -95, -5, 107, 100, 95, -121, 52, -83, 54, -67, -71, 16, 88, 28, -119,
  // -128, 31, 19, -113, -125, -29, 2, -57, 25, -103, -12, -36, 3, -116, 80,
  // -91, 53, 115, -63, 91, 95, -87, -123, 34, -12, -100, 108, -79, -20, -111,
  // -66, 38, 100, 115, -84, 9, -105, 50, -35, 93, -95, 8, 47, 77, -118, -30,
  // -19, -14, 105, -82, -35, 74, -13, -94, 87, -18, 111, 29, 26, 22, 9, -59,
  // 19, 118, 30, -105, -35, -34, -31, 24, -57, 53, 20, 51, 4, -100, 76, 22, 74,
  // -1, -118, 33, -62, 0, 69, -80, 75, -97, 125, 86, 4, 40, 67, -67, 91, 33, 2,
  // 3, 1, 0, 1};
  // assertTrue(Arrays.equals(expectedBytes, bytes));
  // ASN1InputStream ais2 = new ASN1InputStream(bytes);
  // int tag = ais2.read();
  // assertEquals(48, tag);
  // int length = ais2.read();
  // assertEquals(130, length);
  // int size = length & 0x7f;
  // length = 0;
  // for (int i = 0; i < size; i++)
  // {
  // int next = ais2.read();
  //
  // if (next < 0)
  // {
  // throw new EOFException("EOF found reading length");
  // }
  //
  // length = (length << 8) + next;
  // }
  // assertEquals(266, length);
  // ASN1Primitive p = ais2.readObject();
  // ASN1Primitive e = ais2.readObject();
  // // asnObject = ais2.readObject();
  // ais2.close();
  // } finally {
  // ais.close();
  // }
  //
  // x509EncodedKeySpec = new X509EncodedKeySpec(Base64.decode(pubKey));
  // publicKey = keyFactory.generatePublic(x509EncodedKeySpec);
  //
  //
  // // PKCS8EncodedKeySpec privateKeySpec = new
  // PKCS8EncodedKeySpec(rsaPrivKey.getEncoded());
  // // PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);
  //
  // }

  static PublicKey getFromString2(String keystr) throws Exception {
    ASN1InputStream ais = null;
    try {
      byte[] keyBytes = Base64.decode(keystr);
      ais = new ASN1InputStream(keyBytes);
      Object asnObject = ais.readObject();
      ASN1Sequence sequence = (ASN1Sequence) asnObject;
      RSAPublicKeyStructure rsaPubStructure = new RSAPublicKeyStructure(sequence);
      RSAPublicKeySpec keySpec = new RSAPublicKeySpec(rsaPubStructure.getModulus(), rsaPubStructure.getPublicExponent());

      KeyFactory keyFact = KeyFactory.getInstance("RSA", new BouncyCastleProvider());

      return keyFact.generatePublic(keySpec);
    } catch (IOException e) {
      throw e;
    } finally {
      if (ais != null) {
        ais.close();
        ais = null;
      }
    }

  }

  public void testGabunomiRequest() throws Exception {
    // openid://?response_type=id_token&client_id=https://gabunomi.net/seminar/callback.html&request=eyJhbGciOiJSUzI1NiIsIng1dSI6Imh0dHBzOlwvXC9nYWJ1bm9taS5uZXRcL3NlbWluYXJcL3JzYV9wdWJsaWNfa2V5LnBlbSJ9.ewoJInJlc3BvbnNlX3R5cGUiOiAiaWRfdG9rZW4iLAoJInNjb3BlIjogIm9wZW5pZCIsCgkiY2xpZW50X2lkIjogImh0dHBzOi8vZ2FidW5vbWkubmV0L3NlbWluYXIvY2FsbGJhY2suaHRtbCIsCgkicG9saWN5X3VybCI6ICJodHRwczovL2dhYnVub21pLm5ldC9zZW1pbmFyL3BvbGljeS5odG1sIiwKCSJ1c2VyaW5mbyI6IHsKCQkiY2xhaW1zIjogewoJCQkibmFtZSI6IG51bGwsCgkJCSJlbWFpbCI6IG51bGwsCgkJCSJwaWN0dXJlIjogbnVsbAoJCX0KCX0sCgkicmVnaXN0cmF0aW9uIjogewoJCSJhcHBsaWNhdGlvbl9uYW1lIjogIlNhbXBsZSBTZW1pbmFyIiwKCQkibG9nb191cmwiOiAiaHR0cHM6Ly9nYWJ1bm9taS5uZXQvc2VtaW5hci9sb2dvLnBuZyIsCgkJIng1MDlfdXJsIjogImh0dHBzOi8vZ2FidW5vbWkubmV0L3NlbWluYXIvcnNhX3B1YmxpY19rZXkucGVtIgoJfQp9Cg.Faytuhwb2W4CWVz2-10umSieh-bqR7QXqU0bNF39u_D0mGoBD4e3X2b4jZNqPvPADSnQhlBGSJu189iFM5bwFzchnO-quCpj7T2CK_-wkrpL5LUn_WHYMmYlFadmb-a1p-TEo7exU9azMS9cT70-kHNqmTaJziZyiAMoJ0Q4TtyTt1Xbkknc_CQRug3ilNv3bEXSlOlva3HUOY7jQIbYMB3jDL3QxS1wbVYNAjOxCxCDmiNAUJA-BkYe6Tpyj-DUs57IM4wQSp64sqim8RqirJJfFb4bCbNTkC3G8sYfN2_1-qEDpOnWW7N3gjl174TWHbnzVLAZGg_rZm58-wHOLw&state=50853203806ed&nonce=5085320380ad6
    String request = "eyJhbGciOiJSUzI1NiIsIng1dSI6Imh0dHBzOlwvXC9nYWJ1bm9taS5uZXRcL3NlbWluYXJcL3JzYV9wdWJsaWNfa2V5LnBlbSJ9.ewoJInJlc3BvbnNlX3R5cGUiOiAiaWRfdG9rZW4iLAoJInNjb3BlIjogIm9wZW5pZCIsCgkiY2xpZW50X2lkIjogImh0dHBzOi8vZ2FidW5vbWkubmV0L3NlbWluYXIvY2FsbGJhY2suaHRtbCIsCgkicG9saWN5X3VybCI6ICJodHRwczovL2dhYnVub21pLm5ldC9zZW1pbmFyL3BvbGljeS5odG1sIiwKCSJ1c2VyaW5mbyI6IHsKCQkiY2xhaW1zIjogewoJCQkibmFtZSI6IG51bGwsCgkJCSJlbWFpbCI6IG51bGwsCgkJCSJwaWN0dXJlIjogbnVsbAoJCX0KCX0sCgkicmVnaXN0cmF0aW9uIjogewoJCSJhcHBsaWNhdGlvbl9uYW1lIjogIlNhbXBsZSBTZW1pbmFyIiwKCQkibG9nb191cmwiOiAiaHR0cHM6Ly9nYWJ1bm9taS5uZXQvc2VtaW5hci9sb2dvLnBuZyIsCgkJIng1MDlfdXJsIjogImh0dHBzOi8vZ2FidW5vbWkubmV0L3NlbWluYXIvcnNhX3B1YmxpY19rZXkucGVtIgoJfQp9Cg.Faytuhwb2W4CWVz2-10umSieh-bqR7QXqU0bNF39u_D0mGoBD4e3X2b4jZNqPvPADSnQhlBGSJu189iFM5bwFzchnO-quCpj7T2CK_-wkrpL5LUn_WHYMmYlFadmb-a1p-TEo7exU9azMS9cT70-kHNqmTaJziZyiAMoJ0Q4TtyTt1Xbkknc_CQRug3ilNv3bEXSlOlva3HUOY7jQIbYMB3jDL3QxS1wbVYNAjOxCxCDmiNAUJA-BkYe6Tpyj-DUs57IM4wQSp64sqim8RqirJJfFb4bCbNTkC3G8sYfN2_1-qEDpOnWW7N3gjl174TWHbnzVLAZGg_rZm58-wHOLw";
    String[] split = request.split("\\.");
    assertEquals(3, split.length);
    String jwtHeaderStrB64 = split[0];
    assertEquals(
        "eyJhbGciOiJSUzI1NiIsIng1dSI6Imh0dHBzOlwvXC9nYWJ1bm9taS5uZXRcL3NlbWluYXJcL3JzYV9wdWJsaWNfa2V5LnBlbSJ9",
        jwtHeaderStrB64);
    String jwtPayloadStrB64 = split[1];
    assertEquals(
        "ewoJInJlc3BvbnNlX3R5cGUiOiAiaWRfdG9rZW4iLAoJInNjb3BlIjogIm9wZW5pZCIsCgkiY2xpZW50X2lkIjogImh0dHBzOi8vZ2FidW5vbWkubmV0L3NlbWluYXIvY2FsbGJhY2suaHRtbCIsCgkicG9saWN5X3VybCI6ICJodHRwczovL2dhYnVub21pLm5ldC9zZW1pbmFyL3BvbGljeS5odG1sIiwKCSJ1c2VyaW5mbyI6IHsKCQkiY2xhaW1zIjogewoJCQkibmFtZSI6IG51bGwsCgkJCSJlbWFpbCI6IG51bGwsCgkJCSJwaWN0dXJlIjogbnVsbAoJCX0KCX0sCgkicmVnaXN0cmF0aW9uIjogewoJCSJhcHBsaWNhdGlvbl9uYW1lIjogIlNhbXBsZSBTZW1pbmFyIiwKCQkibG9nb191cmwiOiAiaHR0cHM6Ly9nYWJ1bm9taS5uZXQvc2VtaW5hci9sb2dvLnBuZyIsCgkJIng1MDlfdXJsIjogImh0dHBzOi8vZ2FidW5vbWkubmV0L3NlbWluYXIvcnNhX3B1YmxpY19rZXkucGVtIgoJfQp9Cg",
        jwtPayloadStrB64);
    String jwtSignatureStrB64 = split[2];
    assertEquals(
        "Faytuhwb2W4CWVz2-10umSieh-bqR7QXqU0bNF39u_D0mGoBD4e3X2b4jZNqPvPADSnQhlBGSJu189iFM5bwFzchnO-quCpj7T2CK_-wkrpL5LUn_WHYMmYlFadmb-a1p-TEo7exU9azMS9cT70-kHNqmTaJziZyiAMoJ0Q4TtyTt1Xbkknc_CQRug3ilNv3bEXSlOlva3HUOY7jQIbYMB3jDL3QxS1wbVYNAjOxCxCDmiNAUJA-BkYe6Tpyj-DUs57IM4wQSp64sqim8RqirJJfFb4bCbNTkC3G8sYfN2_1-qEDpOnWW7N3gjl174TWHbnzVLAZGg_rZm58-wHOLw",
        jwtSignatureStrB64);

    String jwtHeaderStr = new String(Base64.decodeUrl(jwtHeaderStrB64));
    assertEquals("{\"alg\":\"RS256\",\"x5u\":\"https:\\/\\/gabunomi.net\\/seminar\\/rsa_public_key.pem\"}",
        jwtHeaderStr);
    String jwtPayloadStr = new String(Base64.decodeUrl(jwtPayloadStrB64));
    String expectedPayload = "{\n" + "\t\"response_type\": \"id_token\",\n" + "\t\"scope\": \"openid\",\n"
        + "\t\"client_id\": \"https://gabunomi.net/seminar/callback.html\",\n"
        + "\t\"policy_url\": \"https://gabunomi.net/seminar/policy.html\",\n" + "\t\"userinfo\": {\n"
        + "\t\t\"claims\": {\n" + "\t\t\t\"name\": null,\n" + "\t\t\t\"email\": null,\n" + "\t\t\t\"picture\": null\n"
        + "\t\t}\n" + "\t},\n" + "\t\"registration\": {\n" + "\t\t\"application_name\": \"Sample Seminar\",\n"
        + "\t\t\"logo_url\": \"https://gabunomi.net/seminar/logo.png\",\n"
        + "\t\t\"x509_url\": \"https://gabunomi.net/seminar/rsa_public_key.pem\"\n" + "\t}\n" + "}\n";

    assertEquals(expectedPayload, jwtPayloadStr);

    byte[] hashInput = jwtHeaderStrB64.concat(".").concat(jwtPayloadStrB64).getBytes();

    byte[] digest = CryptoUtils.byteDigest(hashInput, "SHA-256");
    String digestB64 = Base64.encodeBytesNoBreaks(digest);
    // verify hash=gUA7CmyGw9bldSnhmdCg0L0jFHQ7PDtBTyaQbrKRAeE=
    assertEquals("gUA7CmyGw9bldSnhmdCg0L0jFHQ7PDtBTyaQbrKRAeE=", digestB64);

    String publicKeyB64 = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4OTqe0p1tgEoOVtDzjQIyP1Ipo8ivqTIeH4yH9kLzI4fCKx6ggZJ3h9ecj4p5E355umCThN/1doBc/tq18VGlNtyDNxCh45Z1zGYJKwZxaVaWQXlB2gfgnko1D+Zw9KIlipQHtnhJw/qREEIp4YOgaGcSZBCcQQ4DYCOjfTTbKUXSTlrlOgflfgTiyhUFuiKWkoeivwASigL76PtYNYcn+dlYKYB/vSQ2CY7FtaDcr22EdqUDVPLNg1+K1rsvHvllP7iTnXA5IgxT5JELdrkKX9Ek68zDzelOaJxs2tbkkwbqSLQfREzQ/yGAIOW9rZVqlaVBEBzUYzREmeybVq3gwIDAQAB";
    RSAPublicKeySpec publicKey = getFromString(publicKeyB64);

    BigInteger exp = publicKey.getPublicExponent();
    assertEquals("65537", exp.toString());
    BigInteger mod = publicKey.getModulus();
    assertEquals(
        "28390262971464045617888948370374386784016108961598594111378143986296754450905753096859643175686985146399441686275629525699854600247253793482816003333631402811132266699006261679895599983711105363920580818387867239393966268036687308375538294179672748501159435778454870166497571180009520235785776789286548298117616436667049808307027397061130421436354213240228852573741007736385947130231539878499208465003563821747686730770225721963539907580217514772598413184634565455853109810738396427757655541717729727046901929693468073294248262368381939588905266144494924523880161978796418240225955540153253433744770923199299382327171",
        mod.toString());

    assertEquals(
        "AODk6ntKdbYBKDlbQ840CMj9SKaPIr6kyHh+Mh/ZC8yOHwiseoIGSd4fXnI+KeRN+ebpgk4Tf9XaAXP7atfFRpTbcgzcQoeOWdcxmCSsGcWlWlkF5QdoH4J5KNQ/mcPSiJYqUB7Z4ScP6kRBCKeGDoGhnEmQQnEEOA2Ajo3002ylF0k5a5ToH5X4E4soVBboilpKHor8AEooC++j7WDWHJ/nZWCmAf70kNgmOxbWg3K9thHalA1TyzYNfita7Lx75ZT+4k51wOSIMU+SRC3a5Cl/RJOvMw83pTmicbNrW5JMG6ki0H0RM0P8hgCDlva2VapWlQRAc1GM0RJnsm1at4M=",
        Base64.encodeBytesNoBreaks(mod.toByteArray()));

    // WeaveCrypto verify: modulus
    // =4OTqe0p1tgEoOVtDzjQIyP1Ipo8ivqTIeH4yH9kLzI4fCKx6ggZJ3h9ecj4p5E355umCThN/1doBc/tq18VGlNtyDNxCh45Z1zGYJKwZxaVaWQXlB2gfgnko1D+Zw9KIlipQHtnhJw/qREEIp4YOgaGcSZBCcQQ4DYCOjfTTbKUXSTlrlOgflfgTiyhUFuiKWkoeivwASigL76PtYNYcn+dlYKYB/vSQ2CY7FtaDcr22EdqUDVPLNg1+K1rsvHvllP7iTnXA5IgxT5JELdrkKX9Ek68zDzelOaJxs2tbkkwbqSLQfREzQ/yGAIOW9rZVqlaVBEBzUYzREmeybVq3gw==
    String modB64 = "4OTqe0p1tgEoOVtDzjQIyP1Ipo8ivqTIeH4yH9kLzI4fCKx6ggZJ3h9ecj4p5E355umCThN/1doBc/tq18VGlNtyDNxCh45Z1zGYJKwZxaVaWQXlB2gfgnko1D+Zw9KIlipQHtnhJw/qREEIp4YOgaGcSZBCcQQ4DYCOjfTTbKUXSTlrlOgflfgTiyhUFuiKWkoeivwASigL76PtYNYcn+dlYKYB/vSQ2CY7FtaDcr22EdqUDVPLNg1+K1rsvHvllP7iTnXA5IgxT5JELdrkKX9Ek68zDzelOaJxs2tbkkwbqSLQfREzQ/yGAIOW9rZVqlaVBEBzUYzREmeybVq3gw==";
    byte[] modBytes = Base64.decode(modB64);
    BigInteger weaveMod = new BigInteger(1, modBytes);
    assertTrue(weaveMod.equals(mod));

    assertTrue(CryptoUtils.verifyRSA(hashInput, Base64.decodeUrl(jwtSignatureStrB64), mod, exp, "SHA256"));
  }

  // public void testgenerateEncodedEncryptedKeyRSA15() throws Exception {
  // String cmk = "ewiGRJm1Ts1DKQzFjbOispioiNvQ__-BoUmjV4UaIq4";
  // byte[] contentEncryptionKeyBytes = Base64.decodeUrl(cmk);
  // SecretKey contentEncryptionKey = new
  // SecretKeySpec(contentEncryptionKeyBytes, "AES");
  // String gabunomiPublicKeyB64 =
  // "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4OTqe0p1tgEoOVtDzjQIyP1Ipo8ivqTIeH4yH9kLzI4fCKx6ggZJ3h9ecj4p5E355umCThN/1doBc/tq18VGlNtyDNxCh45Z1zGYJKwZxaVaWQXlB2gfgnko1D+Zw9KIlipQHtnhJw/qREEIp4YOgaGcSZBCcQQ4DYCOjfTTbKUXSTlrlOgflfgTiyhUFuiKWkoeivwASigL76PtYNYcn+dlYKYB/vSQ2CY7FtaDcr22EdqUDVPLNg1+K1rsvHvllP7iTnXA5IgxT5JELdrkKX9Ek68zDzelOaJxs2tbkkwbqSLQfREzQ/yGAIOW9rZVqlaVBEBzUYzREmeybVq3gwIDAQAB";
  //
  // X509EncodedKeySpec keySpec = new
  // X509EncodedKeySpec(Base64.decode(gabunomiPublicKeyB64));
  // RSAPublicKey pubKey = (RSAPublicKey)
  // KeyFactory.getInstance("RSA").generatePublic(
  // keySpec);
  //
  // String encodedEncryptedKey =
  // JcBase.generateEncodedEncryptedKeyRSA15(pubKey, contentEncryptionKey);
  // assertEquals("mCcoAjRLTu-g3H6dC_BACKihZKScV22GTxkbo3A-58ijWujP6vw4qUHIN3rXsa2e-C7ooGgKAeQHpYNd5BCcNprexnrz-MtKSCa4S64APDdF72yAFwnDZdBFTzNUZTcrCDbQ2EJIguN2-9IlcSO2Dxj8mFEjtUSQ3vCSUfwL62FddENZ-3nCQueZHfYVAXNmxYHSgUZdF27O79AezCSj-bCjhVhZoDJ5brNKButNFWVPp_HxmhKVEmllt7FdK7d-kk1Qj1H-IMri4RfTPbWcY69Ean7zOYEgfPE_Pwyq47Nj248YAsXUOetyRTOfF62iVXa8_mvGAq456bXJw8mDvA",
  // encodedEncryptedKey);
  // }

  public void testPublicKeyConversion() throws Exception {
    String publicKeyStr = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA65yxqJixG5ZqQUcnQasNiFMwNwAMaQrqf1y-Pa-Nt4ACERwb5DZwNQK5WPhngqcw1KVx5OIiE_AucM6pUJeTVjvMc8_ewdoLAgvHlwh3Es4pZ8_jwvljjbgmt4quu8FYsvdwduZ1bWWOPQf3oFsejeDikZOS0jA38A-6g-ZPRgLik4pyrn2qErLGTKzRMF5_7qH7a2RfhzStNr25EFgciYAfE4-D4wLHGZn03AOMUKU1c8FbX6mFIvScbLHskb4mZHOsCZcy3V2hCC9NiuLt8mmu3Urzolfubx0aFgnFE3Yel93e4RjHNRQzBJxMFkr_iiHCAEWwS599VgQoQ71bIQIDAQAB";
    byte[] bytes = Base64.decodeUrl(publicKeyStr);
    String publicKeyB64 = Base64.encodeBytesNoBreaks(bytes);
    RSAPublicKeySpec spec = getFromString(publicKeyB64);
    String expected = "AOucsaiYsRuWakFHJ0GrDYhTMDcADGkK6n9cvj2vjbeAAhEcG+Q2cDUCuVj4Z4KnMNSlceTiIhPwLnDOqVCXk1Y7zHPP3sHaCwILx5cIdxLOKWfP48L5Y424JreKrrvBWLL3cHbmdW1ljj0H96BbHo3g4pGTktIwN/APuoPmT0YC4pOKcq59qhKyxkys0TBef+6h+2tkX4c0rTa9uRBYHImAHxOPg+MCxxmZ9NwDjFClNXPBW1+phSL0nGyx7JG+JmRzrAmXMt1doQgvTYri7fJprt1K86JX7m8dGhYJxRN2Hpfd3uEYxzUUMwScTBZK/4ohwgBFsEuffVYEKEO9WyE=";
    assertEquals(expected, Base64.encodeBytesNoBreaks(spec.getModulus().toByteArray()));
  }

  public void testPublicKeyNSS() throws Exception {
    String jsonStr = "{"
        + "\"action\": \"keypairGenerated\","
        + "\"pubKey\": \"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA65yxqJixG5ZqQUcnQasNiFMwNwAMaQrqf1y-Pa-Nt4ACERwb5DZwNQK5WPhngqcw1KVx5OIiE_AucM6pUJeTVjvMc8_ewdoLAgvHlwh3Es4pZ8_jwvljjbgmt4quu8FYsvdwduZ1bWWOPQf3oFsejeDikZOS0jA38A-6g-ZPRgLik4pyrn2qErLGTKzRMF5_7qH7a2RfhzStNr25EFgciYAfE4-D4wLHGZn03AOMUKU1c8FbX6mFIvScbLHskb4mZHOsCZcy3V2hCC9NiuLt8mmu3Urzolfubx0aFgnFE3Yel93e4RjHNRQzBJxMFkr_iiHCAEWwS599VgQoQ71bIQIDAQAB\","
        + "\"privKey\": \"7hqZXxtERi5lkJ9dzdpgzyhCDXTg7w0-Eqjda9l8akUPw1kKHuuhYApuIqPU2_buTzUMEEPcDI1S4yqz5pRHgtlTUuMLYP6AQ-oTaxXudD07jRRF9O2IcqWlVE2i3lTuL66gLOc7itHNOloGbwfsnWk1HRsOE1TNZXdYmfK6lRIzgHVnMn51pyQxc1SCbIO_q3AzNOGR-CgDzDbGZ1OkH6JzSXbAMSQyAlRjx4jerbWZzY5NESorrv0Ojb7FzGsM8BsYBhi-LZhASp9SG2X3r4IAJ97vsDXGJcofO7yejvJBr7YQzzx7pe41Fat28O6tKy1PMVJUGea_U0K25lSDJMsUGTgmklinMGdJwZ4ERPMTDHihyG4l14NgX8Eo5nz34zZf1TK5WDyTpmf8-TqE_FT1h5B5vvcDYbNhIkidkXh9NvXFwUSb6ePWNcBHj8nRs91qP9-RFg3uA13m1UoE6ovmkPzgxRKMyDwv8iawUSXu1ACRXU77ein0NAUb--BNoraaKSXp4CLaIJkGkAHbieXxoJEO64jB_CJjCpklsWfWWprmb5G3w1XxK3uH_PDbsG_sr40G8m14Lt7X3dBG0RTRmouBkekIdxu0PWi0MVoiZT3HS4rZjQW_tnTCfCL31l-rintm4AmZkZatErVwJFvQfz4A0BjNV2E0CGbq4_wYidySdStZOGlQPn7LeIdlAkR1a1gOSLj0kRsbR-HktpukAR21WqFHvbsrvsGhkMUyoPHWF01aPD8E4Wdmat_GyZV3fyQp5O_TpGW7spekgAtj1e26TJVToGIogT4eSwtyCSsdop5RelE7EZUvLe0YaAsisEmxk9aR5SCaCNLsST9P1nSgpRUXR3h1VGb_BB-DR_taqdNfsdTy6wH7KPBcI48BguTeKd4C2xM0sZsWuzoCqDto4AY5cNjqwqemS_3GzABMNLbbicKzCBFjMfymQ-E_FzaTLX7M2MApyKFlfFDss-vAdIJm5iIFZ5GC7CzBaLbah5VVSRLG26KNQIjaRJxz926qJWXTa7Z6H2ayUrvXd4pC6AJtwtW_r0aDQ3ST6905SYP3SEEJa6oegCyymJbNGdtLBdzUixEoUBfO4SrdibaFwP0TwHv6F5Vu34jpBipIsdYO4QcerNccNAYjly0qLaMNROlH2qh5biFluXPZq4_NktXNesh9hyNDqDcrgyo1lkeLBSudaBa00QGIlyKRmr6a5Qki3mNRR3EKh7DQUhFIw5it53MvuseItpYGqfMB5cA7s-37ZdB8jhbgSCfjj_QS6_WP7FAfJ5PJ5Nl1ZY-DBdMSKwJTNKawpCabp4r4lLS5kWZi62k-eDb6suA_MgY4_x3KwRYcgeOFp8puEtdtwn9RqkCauDCIraD5h-KVENI88OPgo3mXgmt-VzA2V4Goz1NDJFb5angwI88GCVCWOFSn6DbntuqmZspKFTdpzf2lTgVWfRXGQ9WcX6rke_uV2TvEKabOacimRbZIFpPztxK2GaG2Z_zLzIypiRSpl2Dg2FClPtnCv_yDs-fY8tYF5E_DckSm5sTXSKyp6e_bAa43n5iePYKvQl2J4kikqL8jMc14ofyXBfwJq0AFyqoxOH0onOIDxMxSslS2lcJ2DQ6XT0SIXiCEO9M\","
        + "\"jwtPubKey\": {"
        + "  \"mod\": \"65yxqJixG5ZqQUcnQasNiFMwNwAMaQrqf1y-Pa-Nt4ACERwb5DZwNQK5WPhngqcw1KVx5OIiE_AucM6pUJeTVjvMc8_ewdoLAgvHlwh3Es4pZ8_jwvljjbgmt4quu8FYsvdwduZ1bWWOPQf3oFsejeDikZOS0jA38A-6g-ZPRgLik4pyrn2qErLGTKzRMF5_7qH7a2RfhzStNr25EFgciYAfE4-D4wLHGZn03AOMUKU1c8FbX6mFIvScbLHskb4mZHOsCZcy3V2hCC9NiuLt8mmu3Urzolfubx0aFgnFE3Yel93e4RjHNRQzBJxMFkr_iiHCAEWwS599VgQoQ71bIQ\","
        + "  \"exp\": \"AQAB\"," + "  \"alg\": \"rsa\"" + "}," + "\"salt\": \"LDhOQ2Nx02ti76HFE4I9Ug\","
        + "\"iv\": \"f7ZjUM5LavhRyreFMYLLuw\"," + "\"created\": 1351794506809" + "}";
    JSONObject json = new JSONObject(jsonStr);
    byte[] bytes = Base64.decodeUrl(json.getString("pubKey"));
    String publicKeyB64 = Base64.encodeBytesNoBreaks(bytes);
    RSAPublicKeySpec spec = getFromString(publicKeyB64);

    BigInteger modulus = spec.getModulus();
    BigInteger exponent = spec.getPublicExponent();

    byte[] modulusBytes = modulus.toByteArray();
    byte[] expectedModulusBytes = { 0, (byte) -21, (byte) -100, (byte) -79, (byte) -88, (byte) -104, (byte) -79,
        (byte) 27, (byte) -106, (byte) 106, (byte) 65, (byte) 71, (byte) 39, (byte) 65, (byte) -85, (byte) 13,
        (byte) -120, (byte) 83, (byte) 48, (byte) 55, (byte) 0, (byte) 12, (byte) 105, (byte) 10, (byte) -22,
        (byte) 127, (byte) 92, (byte) -66, (byte) 61, (byte) -81, (byte) -115, (byte) -73, (byte) -128, (byte) 2,
        (byte) 17, (byte) 28, (byte) 27, (byte) -28, (byte) 54, (byte) 112, (byte) 53, (byte) 2, (byte) -71, (byte) 88,
        (byte) -8, (byte) 103, (byte) -126, (byte) -89, (byte) 48, (byte) -44, (byte) -91, (byte) 113, (byte) -28,
        (byte) -30, (byte) 34, (byte) 19, (byte) -16, (byte) 46, (byte) 112, (byte) -50, (byte) -87, (byte) 80,
        (byte) -105, (byte) -109, (byte) 86, (byte) 59, (byte) -52, (byte) 115, (byte) -49, (byte) -34, (byte) -63,
        (byte) -38, (byte) 11, (byte) 2, (byte) 11, (byte) -57, (byte) -105, (byte) 8, (byte) 119, (byte) 18,
        (byte) -50, (byte) 41, (byte) 103, (byte) -49, (byte) -29, (byte) -62, (byte) -7, (byte) 99, (byte) -115,
        (byte) -72, (byte) 38, (byte) -73, (byte) -118, (byte) -82, (byte) -69, (byte) -63, (byte) 88, (byte) -78,
        (byte) -9, (byte) 112, (byte) 118, (byte) -26, (byte) 117, (byte) 109, (byte) 101, (byte) -114, (byte) 61,
        (byte) 7, (byte) -9, (byte) -96, (byte) 91, (byte) 30, (byte) -115, (byte) -32, (byte) -30, (byte) -111,
        (byte) -109, (byte) -110, (byte) -46, (byte) 48, (byte) 55, (byte) -16, (byte) 15, (byte) -70, (byte) -125,
        (byte) -26, (byte) 79, (byte) 70, (byte) 2, (byte) -30, (byte) -109, (byte) -118, (byte) 114, (byte) -82,
        (byte) 125, (byte) -86, (byte) 18, (byte) -78, (byte) -58, (byte) 76, (byte) -84, (byte) -47, (byte) 48,
        (byte) 94, (byte) 127, (byte) -18, (byte) -95, (byte) -5, (byte) 107, (byte) 100, (byte) 95, (byte) -121,
        (byte) 52, (byte) -83, (byte) 54, (byte) -67, (byte) -71, (byte) 16, (byte) 88, (byte) 28, (byte) -119,
        (byte) -128, (byte) 31, (byte) 19, (byte) -113, (byte) -125, (byte) -29, (byte) 2, (byte) -57, (byte) 25,
        (byte) -103, (byte) -12, (byte) -36, (byte) 3, (byte) -116, (byte) 80, (byte) -91, (byte) 53, (byte) 115,
        (byte) -63, (byte) 91, (byte) 95, (byte) -87, (byte) -123, (byte) 34, (byte) -12, (byte) -100, (byte) 108,
        (byte) -79, (byte) -20, (byte) -111, (byte) -66, (byte) 38, (byte) 100, (byte) 115, (byte) -84, (byte) 9,
        (byte) -105, (byte) 50, (byte) -35, (byte) 93, (byte) -95, (byte) 8, (byte) 47, (byte) 77, (byte) -118,
        (byte) -30, (byte) -19, (byte) -14, (byte) 105, (byte) -82, (byte) -35, (byte) 74, (byte) -13, (byte) -94,
        (byte) 87, (byte) -18, (byte) 111, (byte) 29, (byte) 26, (byte) 22, (byte) 9, (byte) -59, (byte) 19,
        (byte) 118, (byte) 30, (byte) -105, (byte) -35, (byte) -34, (byte) -31, (byte) 24, (byte) -57, (byte) 53,
        (byte) 20, (byte) 51, (byte) 4, (byte) -100, (byte) 76, (byte) 22, (byte) 74, (byte) -1, (byte) -118,
        (byte) 33, (byte) -62, (byte) 0, (byte) 69, (byte) -80, (byte) 75, (byte) -97, (byte) 125, (byte) 86, (byte) 4,
        (byte) 40, (byte) 67, (byte) -67, (byte) 91, (byte) 33 };
    assertTrue(Arrays.equals(modulusBytes, expectedModulusBytes));

    String expectedModulus = "AOucsaiYsRuWakFHJ0GrDYhTMDcADGkK6n9cvj2vjbeAAhEcG-Q2cDUCuVj4Z4KnMNSlceTiIhPwLnDOqVCXk1Y7zHPP3sHaCwILx5cIdxLOKWfP48L5Y424JreKrrvBWLL3cHbmdW1ljj0H96BbHo3g4pGTktIwN_APuoPmT0YC4pOKcq59qhKyxkys0TBef-6h-2tkX4c0rTa9uRBYHImAHxOPg-MCxxmZ9NwDjFClNXPBW1-phSL0nGyx7JG-JmRzrAmXMt1doQgvTYri7fJprt1K86JX7m8dGhYJxRN2Hpfd3uEYxzUUMwScTBZK_4ohwgBFsEuffVYEKEO9WyE";
    String modulusB64url = Base64.encodeBytes(modulusBytes, Base64.DONT_BREAK_LINES | Base64.URL);
    assertEquals(expectedModulus, modulusB64url);

    String expectedExponent = "AQAB";
    String exponentB64url = Base64.encodeBytes(exponent.toByteArray(), Base64.DONT_BREAK_LINES | Base64.URL);
    assertEquals(expectedExponent, exponentB64url);

    JSONObject jwtPubKey = json.getJSONObject("jwtPubKey");
    byte[] jwtmodulusBytes = Base64.decodeUrl(jwtPubKey.getString("mod"));
    byte[] expectedjwtmodulusBytes = { -21, (byte) -100, (byte) -79, (byte) -88, (byte) -104, (byte) -79, (byte) 27,
        (byte) -106, (byte) 106, (byte) 65, (byte) 71, (byte) 39, (byte) 65, (byte) -85, (byte) 13, (byte) -120,
        (byte) 83, (byte) 48, (byte) 55, (byte) 0, (byte) 12, (byte) 105, (byte) 10, (byte) -22, (byte) 127, (byte) 92,
        (byte) -66, (byte) 61, (byte) -81, (byte) -115, (byte) -73, (byte) -128, (byte) 2, (byte) 17, (byte) 28,
        (byte) 27, (byte) -28, (byte) 54, (byte) 112, (byte) 53, (byte) 2, (byte) -71, (byte) 88, (byte) -8,
        (byte) 103, (byte) -126, (byte) -89, (byte) 48, (byte) -44, (byte) -91, (byte) 113, (byte) -28, (byte) -30,
        (byte) 34, (byte) 19, (byte) -16, (byte) 46, (byte) 112, (byte) -50, (byte) -87, (byte) 80, (byte) -105,
        (byte) -109, (byte) 86, (byte) 59, (byte) -52, (byte) 115, (byte) -49, (byte) -34, (byte) -63, (byte) -38,
        (byte) 11, (byte) 2, (byte) 11, (byte) -57, (byte) -105, (byte) 8, (byte) 119, (byte) 18, (byte) -50,
        (byte) 41, (byte) 103, (byte) -49, (byte) -29, (byte) -62, (byte) -7, (byte) 99, (byte) -115, (byte) -72,
        (byte) 38, (byte) -73, (byte) -118, (byte) -82, (byte) -69, (byte) -63, (byte) 88, (byte) -78, (byte) -9,
        (byte) 112, (byte) 118, (byte) -26, (byte) 117, (byte) 109, (byte) 101, (byte) -114, (byte) 61, (byte) 7,
        (byte) -9, (byte) -96, (byte) 91, (byte) 30, (byte) -115, (byte) -32, (byte) -30, (byte) -111, (byte) -109,
        (byte) -110, (byte) -46, (byte) 48, (byte) 55, (byte) -16, (byte) 15, (byte) -70, (byte) -125, (byte) -26,
        (byte) 79, (byte) 70, (byte) 2, (byte) -30, (byte) -109, (byte) -118, (byte) 114, (byte) -82, (byte) 125,
        (byte) -86, (byte) 18, (byte) -78, (byte) -58, (byte) 76, (byte) -84, (byte) -47, (byte) 48, (byte) 94,
        (byte) 127, (byte) -18, (byte) -95, (byte) -5, (byte) 107, (byte) 100, (byte) 95, (byte) -121, (byte) 52,
        (byte) -83, (byte) 54, (byte) -67, (byte) -71, (byte) 16, (byte) 88, (byte) 28, (byte) -119, (byte) -128,
        (byte) 31, (byte) 19, (byte) -113, (byte) -125, (byte) -29, (byte) 2, (byte) -57, (byte) 25, (byte) -103,
        (byte) -12, (byte) -36, (byte) 3, (byte) -116, (byte) 80, (byte) -91, (byte) 53, (byte) 115, (byte) -63,
        (byte) 91, (byte) 95, (byte) -87, (byte) -123, (byte) 34, (byte) -12, (byte) -100, (byte) 108, (byte) -79,
        (byte) -20, (byte) -111, (byte) -66, (byte) 38, (byte) 100, (byte) 115, (byte) -84, (byte) 9, (byte) -105,
        (byte) 50, (byte) -35, (byte) 93, (byte) -95, (byte) 8, (byte) 47, (byte) 77, (byte) -118, (byte) -30,
        (byte) -19, (byte) -14, (byte) 105, (byte) -82, (byte) -35, (byte) 74, (byte) -13, (byte) -94, (byte) 87,
        (byte) -18, (byte) 111, (byte) 29, (byte) 26, (byte) 22, (byte) 9, (byte) -59, (byte) 19, (byte) 118,
        (byte) 30, (byte) -105, (byte) -35, (byte) -34, (byte) -31, (byte) 24, (byte) -57, (byte) 53, (byte) 20,
        (byte) 51, (byte) 4, (byte) -100, (byte) 76, (byte) 22, (byte) 74, (byte) -1, (byte) -118, (byte) 33,
        (byte) -62, (byte) 0, (byte) 69, (byte) -80, (byte) 75, (byte) -97, (byte) 125, (byte) 86, (byte) 4, (byte) 40,
        (byte) 67, (byte) -67, (byte) 91, (byte) 33 };
    assertTrue(Arrays.equals(jwtmodulusBytes, expectedjwtmodulusBytes));
    BigInteger jwtPubKeyMod = new BigInteger(1, jwtmodulusBytes);
    BigInteger jwtPubKeyExp = new BigInteger(1, Base64.decodeUrl(jwtPubKey.getString("exp")));
    assertTrue(modulus.equals(jwtPubKeyMod));
    assertTrue(exponent.equals(jwtPubKeyExp));
  }

  public void testSignature() throws Exception {
    String sigB64 = "Faytuhwb2W4CWVz2-10umSieh-bqR7QXqU0bNF39u_D0mGoBD4e3X2b4jZNqPvPADSnQhlBGSJu189iFM5bwFzchnO-quCpj7T2CK_-wkrpL5LUn_WHYMmYlFadmb-a1p-TEo7exU9azMS9cT70-kHNqmTaJziZyiAMoJ0Q4TtyTt1Xbkknc_CQRug3ilNv3bEXSlOlva3HUOY7jQIbYMB3jDL3QxS1wbVYNAjOxCxCDmiNAUJA-BkYe6Tpyj-DUs57IM4wQSp64sqim8RqirJJfFb4bCbNTkC3G8sYfN2_1-qEDpOnWW7N3gjl174TWHbnzVLAZGg_rZm58-wHOLw";
    byte[] sigBytes = Base64.decodeUrl(sigB64);
    assertEquals(256, sigBytes.length);
  }

  public void testSignature20130322() throws Exception {
    String jws = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJodHRwOi8vd3d3LmZhYnJpa2FtLmNvbSIsImlzcyI6Imh0dHA6Ly93d3cuY29udG9zby5jb20iLCJuYmYiOjEzNjM4OTc2NDMsImV4cCI6MTM2MzkzMzY0MywiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvY291bnRyeSI6IlVTQSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6InVzZXJAY29udG9zby5jb20iLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9naXZlbm5hbWUiOiJUb255IiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvaG9tZXBob25lIjoiNTU1LjEyMTIiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJTYWxlcyJ9.W_pR8vMp03HyjfwJC7NjkLI1fGrMojTecp7ncmo7Ch0";
    String[] split = jws.split("\\.");
    byte[] headerBytes = Base64.decodeUrl(split[0]);
    byte[] payloadBytes = Base64.decodeUrl(split[1]);
    // System.out.println("payload:" + new String(payloadBytes));
    byte[] signatureBytes = Base64.decodeUrl(split[2]);
    // {"alg":"HS256","typ":"JWT"}
    JSONObject headerO = new JSONObject(new String(headerBytes));
    assertEquals("HS256", headerO.getString("alg"));
    assertEquals("JWT", headerO.getString("typ"));

    byte[] key = Base64.decodeUrl("Vbxq2mlbGJw8XH-ZoYBnUHmHga8_o_IduvU_Tht70iE");
    String signed = JcBase.sign(payloadBytes, new String(headerBytes), key);
    assertTrue(signed.equals(jws));
    String[] split1 = signed.split("\\.");
    byte[] headerBytes1 = Base64.decodeUrl(split[0]);
    byte[] payloadBytes1 = Base64.decodeUrl(split[1]);
    byte[] signatureBytes1 = Base64.decodeUrl(split[2]);

  }

  public void testSignatureVerifyHMAC_20130322() throws Exception {
    String jws = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJodHRwOi8vd3d3LmZhYnJpa2FtLmNvbSIsImlzcyI6Imh0dHA6Ly93d3cuY29udG9zby5jb20iLCJuYmYiOjEzNjM4OTc2NDMsImV4cCI6MTM2MzkzMzY0MywiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvY291bnRyeSI6IlVTQSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6InVzZXJAY29udG9zby5jb20iLCJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9naXZlbm5hbWUiOiJUb255IiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvaG9tZXBob25lIjoiNTU1LjEyMTIiLCJodHRwOi8vc2NoZW1hcy5taWNyb3NvZnQuY29tL3dzLzIwMDgvMDYvaWRlbnRpdHkvY2xhaW1zL3JvbGUiOiJTYWxlcyJ9.W_pR8vMp03HyjfwJC7NjkLI1fGrMojTecp7ncmo7Ch0";
    byte[] secretKeyBytes = Base64.decodeUrl("Vbxq2mlbGJw8XH-ZoYBnUHmHga8_o_IduvU_Tht70iE");
    assertTrue(JcBase.verify(jws, secretKeyBytes));
  }

  public void testSignatureHMAC_20130322() throws Exception {
    String payload = "{\"aud\":\"https://xmldap.org\",\"iss\":\"openinfocard\",\"nbf\":1363897643,\"exp\":1363933643,\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/country\":\"USA\",\"http://schemas.microsoft.com/ws/2008/06/identity/claims/expired\":true,\"http://schemas.microsoft.com/ws/2008/06/identity/claims/version\":4711,\"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/pi\":3.1415926535}";
    String signed256 = JcBase.sign(payload.getBytes(), hs256, hsKey);
    System.out.println("hs256:" + signed256);
    String signed384 = JcBase.sign(payload.getBytes(), hs384, hsKey);
    System.out.println("hs384:" + signed384);
    String signed512 = JcBase.sign(payload.getBytes(), hs512, hsKey);
    System.out.println("hs512:" + signed512);

  }

  public void testBCECSign256() throws Exception {
    ECKeyPairGenerator keyGenerator = new ECKeyPairGenerator();
    ASN1ObjectIdentifier oid = SECNamedCurves.getOID("secp256r1");
    X9ECParameters x9ECParameters = ECUtil.getNamedCurveByOid(oid);
    ECCurve curve = x9ECParameters.getCurve();
    ECPoint G = x9ECParameters.getG();
    BigInteger n = x9ECParameters.getN();
    BigInteger h = x9ECParameters.getH();
    ECDomainParameters dp = new ECDomainParameters(curve, G, n, h);
    ECKeyGenerationParameters param = new ECKeyGenerationParameters(dp, new SecureRandom());
    keyGenerator.init(param);
    AsymmetricCipherKeyPair kp = keyGenerator.generateKeyPair();
    ECPrivateKeyParameters privKey = (ECPrivateKeyParameters) kp.getPrivate();
    BigInteger d = privKey.getD();
    byte[] dBytes = d.toByteArray();
    String dB64 = Base64.encodeBytes(dBytes, Base64.URL);
    System.out.println("secp256r1 D=" + dB64);
    ECPublicKeyParameters pubKey = (ECPublicKeyParameters) kp.getPublic();
    BigInteger x = pubKey.getQ().getX().toBigInteger();
    byte[] xBytes = x.toByteArray();
    BigInteger y = pubKey.getQ().getY().toBigInteger();
    byte[] yBytes = y.toByteArray();
    String xB64 = Base64.encodeBytes(xBytes, Base64.URL);
    System.out.println("secp256r1 X=" + xB64);
    String yB64 = Base64.encodeBytes(yBytes, Base64.URL);
    System.out.println("secp256r1 Y=" + yB64);

    String signed = JcBase.sign(joeStr.getBytes(), new JSONObject(es256), new BigInteger(1, dBytes));
    assertTrue(JcBase.verify(signed, xBytes, yBytes));
  }

  public void testBCECSign384() throws Exception {
    ECKeyPairGenerator keyGenerator = new ECKeyPairGenerator();
    ASN1ObjectIdentifier oid = SECNamedCurves.getOID("secp384r1");
    X9ECParameters x9ECParameters = ECUtil.getNamedCurveByOid(oid);
    ECCurve curve = x9ECParameters.getCurve();
    ECPoint G = x9ECParameters.getG();
    BigInteger n = x9ECParameters.getN();
    BigInteger h = x9ECParameters.getH();
    ECDomainParameters dp = new ECDomainParameters(curve, G, n, h);
    ECKeyGenerationParameters param = new ECKeyGenerationParameters(dp, new SecureRandom());
    keyGenerator.init(param);
    AsymmetricCipherKeyPair kp = keyGenerator.generateKeyPair();
    ECPrivateKeyParameters privKey = (ECPrivateKeyParameters) kp.getPrivate();
    BigInteger d = privKey.getD();
    byte[] dBytes = d.toByteArray();
    String dB64 = Base64.encodeBytes(dBytes, Base64.URL);
    System.out.println("secp384r1 D=" + dB64);
    ECPublicKeyParameters pubKey = (ECPublicKeyParameters) kp.getPublic();
    BigInteger x = pubKey.getQ().getX().toBigInteger();
    byte[] xBytes = x.toByteArray();
    BigInteger y = pubKey.getQ().getY().toBigInteger();
    byte[] yBytes = y.toByteArray();
    String xB64 = Base64.encodeBytes(xBytes, Base64.URL);
    System.out.println("secp384r1 X=" + xB64);
    String yB64 = Base64.encodeBytes(yBytes, Base64.URL);
    System.out.println("secp384r1 Y=" + yB64);

    ECDSASigner signer = new ECDSASigner();
    signer.init(true, privKey);
    BigInteger[] res = signer.generateSignature(joeStr.getBytes());
    BigInteger r = res[0];
    BigInteger s = res[1];
    ECDSASigner verifier = new ECDSASigner();
    verifier.init(false, pubKey);
    verifier.verifySignature(joeStr.getBytes(), r, s);
  }

  public void testECSign256() throws Exception {
    ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp256r1");
    KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("EC");
    keyGenerator.initialize(ecSpec);
    KeyPair keyPair = keyGenerator.generateKeyPair();
    ECPrivateKey priv = (ECPrivateKey) keyPair.getPrivate();
    BigInteger d = priv.getS();
    byte[] dBytes = d.toByteArray();
    String dB64 = Base64.encodeBytes(dBytes, Base64.URL);
    System.out.println("secp256r1 d=" + dB64);
    ECPublicKey pub = (ECPublicKey) keyPair.getPublic();
    byte[] xBytes = pub.getW().getAffineX().toByteArray();
    byte[] yBytes = pub.getW().getAffineY().toByteArray();
    String xB64 = Base64.encodeBytes(xBytes, Base64.URL);
    System.out.println("secp256r1 x=" + xB64);
    String yB64 = Base64.encodeBytes(yBytes, Base64.URL);
    System.out.println("secp256r1 y=" + yB64);

    String signed = JcBase.sign(joeStr.getBytes(), new JSONObject(es256), new BigInteger(1, dBytes));
    assertTrue(JcBase.verify(signed, xBytes, yBytes));
  }

  public void testECSign256Fixed() throws Exception {
    String secp256r1_d_B64 = "AO9rkWQ2Vs_JnkfOcFbtKNcLDKg7QVUJgdoh4V0dRnur";
    String secp256r1_x_B64 = "ANdR243_ZSv5MTAbQe25T5HmPDRET9eJehSkwvhADXuX";
    String secp256r1_y_B64 = "UBs1WbqRfALcold-cga_iYq2QN4f9dQz4PX4eUswg68";

    byte[] dBytes = Base64.decodeUrl(secp256r1_d_B64);
    String signed = JcBase.sign(joeStr.getBytes(), new JSONObject(es256), new BigInteger(1, dBytes));

    String[] split = signed.split("\\.");
    assertEquals(3, split.length);
    assertEquals("eyJhbGciOiJFUzI1NiJ9", split[0]);
    assertEquals("eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
        split[1]);
    byte[] signatureBytes = Base64.decodeUrl(split[2]);
    assertEquals(64, signatureBytes.length);
    byte[] xBytes = Base64.decodeUrl(secp256r1_x_B64);
    byte[] yBytes = Base64.decodeUrl(secp256r1_y_B64);
    assertTrue(JcBase.verify(signed, xBytes, yBytes));

  }

  public void testECSign384() throws Exception {
    ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp384r1");
    KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("EC");
    keyGenerator.initialize(ecSpec);
    KeyPair keyPair = keyGenerator.generateKeyPair();
    ECPrivateKey priv = (ECPrivateKey) keyPair.getPrivate();
    BigInteger d = priv.getS();
    byte[] dBytes = d.toByteArray();
    String dB64 = Base64.encodeBytes(dBytes, Base64.URL);
    System.out.println("secp384r1 d=" + dB64);
    ECPublicKey pub = (ECPublicKey) keyPair.getPublic();
    byte[] xBytes = pub.getW().getAffineX().toByteArray();
    byte[] yBytes = pub.getW().getAffineY().toByteArray();
    String xB64 = Base64.encodeBytes(xBytes, Base64.URL);
    System.out.println("secp384r1 x=" + xB64);
    String yB64 = Base64.encodeBytes(yBytes, Base64.URL);
    System.out.println("secp384r1 y=" + yB64);

    String signed = JcBase.sign(joeStr.getBytes(), new JSONObject(es384), new BigInteger(1, dBytes));
    assertTrue(JcBase.verify(signed, xBytes, yBytes));
  }

  public void testECSign384fixed() throws Exception {
    String secp384r1_d_B64 = "FOyrzGT4rkgNlchAolmav7v9ABzg4gmvP9q7aW3DYg5eqyW-etl7-M9yyjYEc1_3";
    String secp384r1_x_B64 = "LX4VQxtOyXjABmeqCHmy7vtMnoNosGIxvgYfRGGoC_bS1WqtS1S2J0SM0cOWiqRi";
    String secp384r1_y_B64 = "APeEpAaQo7PdhwACeVuLC20QyaBG6DAWj4PFcb9_CvTSx_6Z3JGVLFuWx7T7BXEuPw";
    byte[] dBytes = Base64.decodeUrl(secp384r1_d_B64);
    String signed = JcBase.sign(joeStr.getBytes(), new JSONObject(es384), new BigInteger(1, dBytes));

    String[] split = signed.split("\\.");
    assertEquals(3, split.length);
    assertEquals("eyJhbGciOiJFUzM4NCJ9", split[0]);
    assertEquals("eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
        split[1]);
    byte[] signatureBytes = Base64.decodeUrl(split[2]);
    assertEquals(96, signatureBytes.length);
    byte[] xBytes = Base64.decodeUrl(secp384r1_x_B64);
    byte[] yBytes = Base64.decodeUrl(secp384r1_y_B64);
    assertTrue(JcBase.verify(signed, xBytes, yBytes));
  }

  public void testECSign512() throws Exception {
    ECGenParameterSpec ecSpec = new ECGenParameterSpec("secp521r1");
    KeyPairGenerator keyGenerator = KeyPairGenerator.getInstance("EC");
    keyGenerator.initialize(ecSpec);
    KeyPair keyPair = keyGenerator.generateKeyPair();
    ECPrivateKey priv = (ECPrivateKey) keyPair.getPrivate();
    BigInteger d = priv.getS();
    byte[] dBytes = d.toByteArray();
    String dB64 = Base64.encodeBytes(dBytes, Base64.URL);
    System.out.println("String secp521r1_d_B64=\"" + dB64 + "\";");
    ECPublicKey pub = (ECPublicKey) keyPair.getPublic();
    byte[] xBytes = pub.getW().getAffineX().toByteArray();
    byte[] yBytes = pub.getW().getAffineY().toByteArray();
    String xB64 = Base64.encodeBytes(xBytes, Base64.URL);
    System.out.println("String secp521r1_x_B64=\"" + xB64 + "\";");
    String yB64 = Base64.encodeBytes(yBytes, Base64.URL);
    System.out.println("String secp521r1r1_y_B64=\"" + yB64 + "\";");

    String signed = JcBase.sign(joeStr.getBytes(), new JSONObject(es512), new BigInteger(1, dBytes));
    assertTrue(JcBase.verify(signed, xBytes, yBytes));
  }

  public void testECSign512fixed() throws Exception {
    String secp521r1_d_B64 = "AUcR8CxeLgHKKAmjTv_0Khmhn9fgoZXMtE5d7s9CHjSEVGWpNn8EuLCdHl5MJF9drFmvZWdlEeHjb9GkJKXYNqv3";
    String secp521r1_x_B64 = "AJB6qj6hsRSOSzbxOjkvYcPEiHPJZcKGY6L0p6HlRMKhbAwzMbCetldN9rooZz9e4qFMLrA5yfumlR106EA29m7D";
    String secp521r1_y_B64 = "AZ4L8rME2bFhUD-csRmKyUoRTfgeY5uyN8hO7Oea6OPO3eK2bcqsStSrECeEAA8US9OoEChxdsNAQlt77_8QKylK";
    byte[] dBytes = Base64.decodeUrl(secp521r1_d_B64);
    String signed = JcBase.sign(joeStr.getBytes(), new JSONObject(es512), new BigInteger(1, dBytes));

    String[] split = signed.split("\\.");
    assertEquals(3, split.length);
    assertEquals("eyJhbGciOiJFUzUxMiJ9", split[0]);
    assertEquals("eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
        split[1]);
    byte[] signatureBytes = Base64.decodeUrl(split[2]);
    assertEquals(132, signatureBytes.length);
    byte[] xBytes = Base64.decodeUrl(secp521r1_x_B64);
    byte[] yBytes = Base64.decodeUrl(secp521r1_y_B64);
    assertTrue(JcBase.verify(signed, xBytes, yBytes));
  }
  
  private JSONObject testRSAtoJWK(int keyLength) throws Exception {
    KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", new BouncyCastleProvider());
    keyGen.initialize(keyLength, new SecureRandom());
    KeyPair keyPair = keyGen.generateKeyPair();

    RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic();
    
    JSONObject jwk = JcBase.toJWK(rsaPublicKey, null);
    System.out.println("String jwkStr = " + jwk.toString() + ";");
    String nB64 = jwk.getString("n");
    byte[] nBytes = Base64.decodeUrl(nB64);
    assertEquals(keyLength/8, nBytes.length);
    return jwk;
  }
  
  public void testJWK2048() throws Exception {
    JSONObject jwk = testRSAtoJWK(2048);
    RSAPublicKey rsaPublicKey = null;
    rsaPublicKey = JcBase.fromJWK(jwk, rsaPublicKey);
    assertEquals(2048, rsaPublicKey.getModulus().bitLength());

  }
  
  public void testJWK1024() throws Exception {
    JSONObject jwk = testRSAtoJWK(1024);
    RSAPublicKey rsaPublicKey = null;
    rsaPublicKey = JcBase.fromJWK(jwk, rsaPublicKey);
    assertEquals(1024, rsaPublicKey.getModulus().bitLength());
  }
  
  public void testRSAfromJWK() throws Exception {
    String jwkStr = "{\"e\":\"AQAB\",\"n\":\"gjlj-gcPDcOo2Q-ZIGm_nLQAFl2VwsWWyUb5t9qy87xPTG62lOQSHdA24CXJwTS56BoLJiRqjWr0TFlEWdFLga46AdLk4qHZNhiv1VWGYE3IkNXmt-pi9m1gq-OhKl0z-VcCDA24LKHakXaQBoq42u8Q6ZKrRQTq3h_0wyC50bE\",\"kty\":\"RSA\"}";
    JSONObject jwk = new JSONObject(jwkStr);
    RSAPublicKey rsaPublicKey = null;
    rsaPublicKey = JcBase.fromJWK(jwk, rsaPublicKey);
    assertEquals(1024, rsaPublicKey.getModulus().bitLength());
  }
}
